update libusb to 1.0.26. (#877)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Mon, 23 May 2022 22:19:19 +0000 (16:19 -0600)
committerGitHub <noreply@github.com>
Mon, 23 May 2022 22:19:19 +0000 (16:19 -0600)
The following warnings are resolved in 1.0.25, but the lateset release is 1.0.26.
warning: 'kIOMasterPortDefault' is deprecated: first deprecated in macOS 12.0 [-Wdeprecated-declarations]

17 files changed:
libusb.cmake
libusb.pri
mac/libusb/README
mac/libusb/core.c
mac/libusb/descriptor.c
mac/libusb/hotplug.c
mac/libusb/hotplug.h [deleted file]
mac/libusb/io.c
mac/libusb/libusb.h
mac/libusb/libusb.pro
mac/libusb/libusbi.h
mac/libusb/os/darwin_usb.c
mac/libusb/os/darwin_usb.h
mac/libusb/os/events_posix.c
mac/libusb/sync.c
mac/libusb/version.h
mac/libusb/version_nano.h

index 171193fe2151a2aca755602dcd7f54f98b523254..1b973f1fae01b94698c7d41b4417a1ba67c8a32f 100644 (file)
@@ -41,7 +41,6 @@ if(UNIX)
             mac/libusb/os/darwin_usb.c
             mac/libusb/os/events_posix.c
             mac/libusb/os/threads_posix.c
-            mac/libusb/hotplug.h
             mac/libusb/libusb.h
             mac/libusb/libusbi.h
             mac/libusb/version.h
@@ -52,7 +51,7 @@ if(UNIX)
             mac/libusb/XCode/config.h
           )
           target_include_directories(usb-1.0 PRIVATE mac/libusb/XCode PUBLIC mac/libusb)
-          target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation")
+          target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation" "-framework Security")
           list(APPEND LIBS usb-1.0)
         elseif(GPSBABEL_WITH_LIBUSB STREQUAL "custom")
           message(STATUS "libusb-1.0 is enabled but but must be manually configured.")
index 9d589001fefb4db11eed47bcc7d63feabbf0cfe4..4b8cc2ee7695d510233844962a4f30141dd05e11 100644 (file)
@@ -32,7 +32,7 @@ unix {
           # TODO: It would be better to create an archive and link to it
           #       to separate library build requirements from gpsbabel requirements.
           DEFINES += LIBUSB_H_INCLUDE=$$shell_quote(\"mac/libusb/libusb.h\")
-          LIBS += -lobjc -framework IOKit -framework CoreFoundation
+          LIBS += -lobjc -framework IOKit -framework CoreFoundation -framework Security
           INCLUDEPATH += mac/libusb \
                          mac/libusb/Xcode
           SOURCES += \
@@ -46,7 +46,6 @@ unix {
             mac/libusb/os/events_posix.c \
             mac/libusb/os/threads_posix.c
           HEADERS += \
-            mac/libusb/hotplug.h \
             mac/libusb/libusb.h \
             mac/libusb/libusbi.h \
             mac/libusb/version.h \
index 77ef9b8a0822a942f853d1fe03245e1a48eb4fdf..47aaf8106b7db2fceb1f9b6286a45c47f30cde2f 100644 (file)
@@ -1,4 +1,4 @@
-This is libusb-1.0.24 from https://libusb.info/.
+This is libusb-1.0.26 from https://libusb.info/.
 Since we have such problems with people
 getting libusb successfully built - between the Universal Build issues
 and the fact that we have to work hard to go find where it's installed
index 07d459c1269c64b39ce2e83b59bec9a3fd9626ab..ec429b7cf17e8205fffb07e3acb1697535efcb73 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 #include "libusbi.h"
-#include "hotplug.h"
 #include "version.h"
 
 #ifdef __ANDROID__
 #include <syslog.h>
 #endif
 
-struct libusb_context *usbi_default_context;
 static const struct libusb_version libusb_version_internal =
        { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
          LIBUSB_RC, "http://libusb.info" };
-static int default_context_refcnt;
-static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
 static struct timespec timestamp_origin;
 #if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
 static libusb_log_cb log_handler;
 #endif
 
+struct libusb_context *usbi_default_context;
+struct libusb_context *usbi_fallback_context;
+static int default_context_refcnt;
+static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct usbi_option default_context_options[LIBUSB_OPTION_MAX];
+
+
 usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
 struct list_head active_contexts_list;
 
@@ -310,7 +313,8 @@ if (cfg != desired)
  * - libusb is able to send a packet of zero length to an endpoint simply by
  * submitting a transfer of zero length.
  * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET
- * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux.
+ * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux,
+ * Darwin and Windows (WinUSB).
  */
 
 /**
@@ -577,7 +581,9 @@ libusb_free_device_list(list, 1);
  *
  * The libusb_get_device_list() function can be used to obtain a list of
  * devices currently connected to the system. This is known as device
- * discovery.
+ * discovery. Devices can also be discovered with the hotplug mechanism,
+ * whereby a callback function registered with libusb_hotplug_register_callback()
+ * will be called when a device of interest is connected or disconnected.
  *
  * Just because you have a reference to a device does not mean it is
  * necessarily usable. The device may have been unplugged, you may not have
@@ -608,7 +614,7 @@ libusb_free_device_list(list, 1);
  *
  * With the above information in mind, the process of opening a device can
  * be viewed as follows:
- * -# Discover devices using libusb_get_device_list().
+ * -# Discover devices using libusb_get_device_list() or libusb_hotplug_register_callback().
  * -# Choose the device that you want to operate, and call libusb_open().
  * -# Unref all devices in the discovered device list.
  * -# Free the discovered device list.
@@ -633,7 +639,7 @@ libusb_free_device_list(list, 1);
  * which grows when required. it can be freed once discovery has completed,
  * eliminating the need for a list node in the libusb_device structure
  * itself. */
-#define DISCOVERED_DEVICES_SIZE_STEP 8
+#define DISCOVERED_DEVICES_SIZE_STEP 16
 
 static struct discovered_devs *discovered_devs_alloc(void)
 {
@@ -674,7 +680,7 @@ struct discovered_devs *discovered_devs_append(
        }
 
        /* exceeded capacity, need to grow */
-       usbi_dbg("need to increase capacity");
+       usbi_dbg(DEVICE_CTX(dev), "need to increase capacity");
        capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
        /* can't use usbi_reallocf here because in failure cases it would
         * free the existing discdevs without unreferencing its devices. */
@@ -704,16 +710,14 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
        if (!dev)
                return NULL;
 
-       usbi_mutex_init(&dev->lock);
+       usbi_atomic_store(&dev->refcnt, 1);
 
        dev->ctx = ctx;
-       dev->refcnt = 1;
        dev->session_data = session_id;
        dev->speed = LIBUSB_SPEED_UNKNOWN;
 
-       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
-               usbi_connect_device (dev);
-       }
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+               usbi_connect_device(dev);
 
        return dev;
 }
@@ -722,39 +726,26 @@ void usbi_connect_device(struct libusb_device *dev)
 {
        struct libusb_context *ctx = DEVICE_CTX(dev);
 
-       dev->attached = 1;
+       usbi_atomic_store(&dev->attached, 1);
 
        usbi_mutex_lock(&dev->ctx->usb_devs_lock);
        list_add(&dev->list, &dev->ctx->usb_devs);
        usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
 
-       /* Signal that an event has occurred for this device if we support hotplug AND
-        * the hotplug message list is ready. This prevents an event from getting raised
-        * during initial enumeration. */
-       if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
-               usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
-       }
+       usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
 }
 
 void usbi_disconnect_device(struct libusb_device *dev)
 {
        struct libusb_context *ctx = DEVICE_CTX(dev);
 
-       usbi_mutex_lock(&dev->lock);
-       dev->attached = 0;
-       usbi_mutex_unlock(&dev->lock);
+       usbi_atomic_store(&dev->attached, 0);
 
        usbi_mutex_lock(&ctx->usb_devs_lock);
        list_del(&dev->list);
        usbi_mutex_unlock(&ctx->usb_devs_lock);
 
-       /* Signal that an event has occurred for this device if we support hotplug AND
-        * the hotplug message list is ready. This prevents an event from getting raised
-        * during initial enumeration. libusb_handle_events will take care of dereferencing
-        * the device. */
-       if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
-               usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
-       }
+       usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
 }
 
 /* Perform some final sanity checks on a newly discovered device. If this
@@ -775,7 +766,7 @@ int usbi_sanitize_device(struct libusb_device *dev)
                usbi_err(DEVICE_CTX(dev), "too many configurations");
                return LIBUSB_ERROR_IO;
        } else if (0 == num_configurations) {
-               usbi_dbg("zero configurations, maybe an unauthorized device");
+               usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device");
        }
 
        return 0;
@@ -830,7 +821,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
        int r = 0;
        ssize_t i, len;
 
-       usbi_dbg(" ");
+       usbi_dbg(ctx, " ");
 
        if (!discdevs)
                return LIBUSB_ERROR_NO_MEM;
@@ -1173,9 +1164,11 @@ out:
 DEFAULT_VISIBILITY
 libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
 {
-       usbi_mutex_lock(&dev->lock);
-       dev->refcnt++;
-       usbi_mutex_unlock(&dev->lock);
+       long refcnt;
+
+       refcnt = usbi_atomic_inc(&dev->refcnt);
+       assert(refcnt >= 2);
+
        return dev;
 }
 
@@ -1186,17 +1179,16 @@ libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
  */
 void API_EXPORTED libusb_unref_device(libusb_device *dev)
 {
-       int refcnt;
+       long refcnt;
 
        if (!dev)
                return;
 
-       usbi_mutex_lock(&dev->lock);
-       refcnt = --dev->refcnt;
-       usbi_mutex_unlock(&dev->lock);
+       refcnt = usbi_atomic_dec(&dev->refcnt);
+       assert(refcnt >= 0);
 
        if (refcnt == 0) {
-               usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+               usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address);
 
                libusb_unref_device(dev->parent_dev);
 
@@ -1208,7 +1200,6 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
                        usbi_disconnect_device(dev);
                }
 
-               usbi_mutex_destroy(&dev->lock);
                free(dev);
        }
 }
@@ -1218,8 +1209,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
  * handle for the underlying device. The handle allows you to use libusb to
  * perform I/O on the device in question.
  *
- * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY)
- * before libusb_init if don't have authority to access the usb device directly.
+ * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before
+ * libusb_init() if you want to skip enumeration of USB devices. In particular,
+ * this might be needed on Android if you don't have authority to access USB
+ * devices in general.
  *
  * On Linux, the system device handle must be a valid file descriptor opened
  * on the device node.
@@ -1233,6 +1226,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
  *
  * This is a non-blocking function; no requests are sent over the bus.
  *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
  * \param ctx the context to operate on, or NULL for the default context
  * \param sys_dev the platform-specific system device handle
  * \param dev_handle output location for the returned device handle pointer. Only
@@ -1251,7 +1246,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
        size_t priv_size = usbi_backend.device_handle_priv_size;
        int r;
 
-       usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
+       usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
 
        ctx = usbi_get_context(ctx);
 
@@ -1266,7 +1261,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
 
        r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev);
        if (r < 0) {
-               usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
+               usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
                usbi_mutex_destroy(&_dev_handle->lock);
                free(_dev_handle);
                return r;
@@ -1306,11 +1301,11 @@ int API_EXPORTED libusb_open(libusb_device *dev,
        struct libusb_device_handle *_dev_handle;
        size_t priv_size = usbi_backend.device_handle_priv_size;
        int r;
-       usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
 
-       if (!dev->attached) {
+       usbi_dbg(DEVICE_CTX(dev), "open %d.%d", dev->bus_number, dev->device_address);
+
+       if (!usbi_atomic_load(&dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
-       }
 
        _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
        if (!_dev_handle)
@@ -1322,7 +1317,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
 
        r = usbi_backend.open(_dev_handle);
        if (r < 0) {
-               usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
+               usbi_dbg(DEVICE_CTX(dev), "open %d.%d returns %d", dev->bus_number, dev->device_address, r);
                libusb_unref_device(dev);
                usbi_mutex_destroy(&_dev_handle->lock);
                free(_dev_handle);
@@ -1428,7 +1423,7 @@ static void do_close(struct libusb_context *ctx,
                 * just making sure that we don't attempt to process the transfer after
                 * the device handle is invalid
                 */
-               usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed",
+               usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed",
                         transfer, dev_handle);
        }
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
@@ -1462,9 +1457,9 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
 
        if (!dev_handle)
                return;
-       usbi_dbg(" ");
-
        ctx = HANDLE_CTX(dev_handle);
+       usbi_dbg(ctx, " ");
+
        handling_events = usbi_handling_events(ctx);
 
        /* Similarly to libusb_open(), we want to interrupt all event handlers
@@ -1546,27 +1541,28 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
 {
        int r = LIBUSB_ERROR_NOT_SUPPORTED;
        uint8_t tmp = 0;
+       struct libusb_context *ctx = HANDLE_CTX(dev_handle);
 
-       usbi_dbg(" ");
+       usbi_dbg(ctx, " ");
        if (usbi_backend.get_configuration)
                r = usbi_backend.get_configuration(dev_handle, &tmp);
 
        if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
-               usbi_dbg("falling back to control message");
+               usbi_dbg(ctx, "falling back to control message");
                r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
                        LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
                if (r == 1) {
                        r = 0;
                } else if (r == 0) {
-                       usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
+                       usbi_err(ctx, "zero bytes returned in ctrl transfer?");
                        r = LIBUSB_ERROR_IO;
                } else {
-                       usbi_dbg("control failed, error %d", r);
+                       usbi_dbg(ctx, "control failed, error %d", r);
                }
        }
 
        if (r == 0) {
-               usbi_dbg("active config %u", tmp);
+               usbi_dbg(ctx, "active config %u", tmp);
                *config = (int)tmp;
        }
 
@@ -1630,7 +1626,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
        int configuration)
 {
-       usbi_dbg("configuration %d", configuration);
+       usbi_dbg(HANDLE_CTX(dev_handle), "configuration %d", configuration);
        if (configuration < -1 || configuration > (int)UINT8_MAX)
                return LIBUSB_ERROR_INVALID_PARAM;
        return usbi_backend.set_configuration(dev_handle, configuration);
@@ -1669,11 +1665,11 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
 {
        int r = 0;
 
-       usbi_dbg("interface %d", interface_number);
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        usbi_mutex_lock(&dev_handle->lock);
@@ -1713,7 +1709,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
 {
        int r;
 
-       usbi_dbg("interface %d", interface_number);
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
@@ -1756,19 +1752,19 @@ out:
 int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
        int interface_number, int alternate_setting)
 {
-       usbi_dbg("interface %d altsetting %d",
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d altsetting %d",
                interface_number, alternate_setting);
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
        if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       usbi_mutex_lock(&dev_handle->lock);
-       if (!dev_handle->dev->attached) {
+       if (!usbi_atomic_load(&dev_handle->dev->attached)) {
                usbi_mutex_unlock(&dev_handle->lock);
                return LIBUSB_ERROR_NO_DEVICE;
        }
 
+       usbi_mutex_lock(&dev_handle->lock);
        if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
                usbi_mutex_unlock(&dev_handle->lock);
                return LIBUSB_ERROR_NOT_FOUND;
@@ -1798,8 +1794,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
 int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
        unsigned char endpoint)
 {
-       usbi_dbg("endpoint %x", endpoint);
-       if (!dev_handle->dev->attached)
+       usbi_dbg(HANDLE_CTX(dev_handle), "endpoint 0x%x", endpoint);
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        return usbi_backend.clear_halt(dev_handle, endpoint);
@@ -1826,8 +1822,8 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
  */
 int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
 {
-       usbi_dbg(" ");
-       if (!dev_handle->dev->attached)
+       usbi_dbg(HANDLE_CTX(dev_handle), " ");
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.reset_device)
@@ -1860,12 +1856,12 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
 int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
        uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
 {
-       usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+       usbi_dbg(HANDLE_CTX(dev_handle), "streams %u eps %d", (unsigned)num_streams, num_endpoints);
 
        if (!num_streams || !endpoints || num_endpoints <= 0)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.alloc_streams)
@@ -1890,12 +1886,12 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
        unsigned char *endpoints, int num_endpoints)
 {
-       usbi_dbg("eps %d", num_endpoints);
+       usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints);
 
        if (!endpoints || num_endpoints <= 0)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.free_streams)
@@ -1933,7 +1929,7 @@ DEFAULT_VISIBILITY
 unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
         size_t length)
 {
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return NULL;
 
        if (usbi_backend.dev_mem_alloc)
@@ -1979,12 +1975,12 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
        int interface_number)
 {
-       usbi_dbg("interface %d", interface_number);
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
 
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.kernel_driver_active)
@@ -1997,7 +1993,7 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
  * Detach a kernel driver from an interface. If successful, you will then be
  * able to claim the interface and perform I/O.
  *
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
  *
  * Note that libusb itself also talks to the device through a special kernel
  * driver, if this driver is already attached to the device, this call will
@@ -2017,12 +2013,12 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
        int interface_number)
 {
-       usbi_dbg("interface %d", interface_number);
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
 
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.detach_kernel_driver)
@@ -2033,10 +2029,9 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
 
 /** \ingroup libusb_dev
  * Re-attach an interface's kernel driver, which was previously detached
- * using libusb_detach_kernel_driver(). This call is only effective on
- * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ * using libusb_detach_kernel_driver().
  *
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
  *
  * \param dev_handle a device handle
  * \param interface_number the interface to attach the driver from
@@ -2054,12 +2049,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
        int interface_number)
 {
-       usbi_dbg("interface %d", interface_number);
+       usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
 
        if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       if (!dev_handle->dev->attached)
+       if (!usbi_atomic_load(&dev_handle->dev->attached))
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.attach_kernel_driver)
@@ -2130,6 +2125,8 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
  * If ENABLE_DEBUG_LOGGING is defined then per context callback function will
  * never be called.
  *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
  * \param ctx context on which to assign log handler, or NULL for the default
  * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested.
  * \param cb pointer to the callback function, or NULL to stop log
@@ -2170,6 +2167,9 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
  * Some options require one or more arguments to be provided. Consult each
  * option's documentation for specific requirements.
  *
+ * If the context ctx is NULL, the option will be added to a list of default
+ * options that will be applied to all subsequently created contexts.
+ *
  * Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106
  *
  * \param ctx context on which to operate
@@ -2185,40 +2185,63 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
 int API_EXPORTED libusb_set_option(libusb_context *ctx,
        enum libusb_option option, ...)
 {
-       int arg, r = LIBUSB_SUCCESS;
+       int arg = 0, r = LIBUSB_SUCCESS;
        va_list ap;
 
-       ctx = usbi_get_context(ctx);
-
        va_start(ap, option);
-       switch (option) {
-       case LIBUSB_OPTION_LOG_LEVEL:
+       if (LIBUSB_OPTION_LOG_LEVEL == option) {
                arg = va_arg(ap, int);
                if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
                        r = LIBUSB_ERROR_INVALID_PARAM;
-                       break;
                }
+       }
+       va_end(ap);
+
+       if (LIBUSB_SUCCESS != r) {
+               return r;
+       }
+
+       if (option >= LIBUSB_OPTION_MAX) {
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       if (NULL == ctx) {
+               usbi_mutex_static_lock(&default_context_lock);
+               default_context_options[option].is_set = 1;
+               if (LIBUSB_OPTION_LOG_LEVEL == option) {
+                       default_context_options[option].arg.ival = arg;
+               }
+               usbi_mutex_static_unlock(&default_context_lock);
+       }
+
+       ctx = usbi_get_context(ctx);
+       if (NULL == ctx) {
+               return LIBUSB_SUCCESS;
+       }
+
+       switch (option) {
+       case LIBUSB_OPTION_LOG_LEVEL:
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
                if (!ctx->debug_fixed)
                        ctx->debug = (enum libusb_log_level)arg;
 #endif
                break;
 
-       /* Handle all backend-specific options here */
+               /* Handle all backend-specific options here */
        case LIBUSB_OPTION_USE_USBDK:
-       case LIBUSB_OPTION_WEAK_AUTHORITY:
+       case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
                if (usbi_backend.set_option)
-                       r = usbi_backend.set_option(ctx, option, ap);
-               else
-                       r = LIBUSB_ERROR_NOT_SUPPORTED;
+                       return usbi_backend.set_option(ctx, option, ap);
+
+               return LIBUSB_ERROR_NOT_SUPPORTED;
                break;
 
+       case LIBUSB_OPTION_MAX:
        default:
-               r = LIBUSB_ERROR_INVALID_PARAM;
+               return LIBUSB_ERROR_INVALID_PARAM;
        }
-       va_end(ap);
 
-       return r;
+       return LIBUSB_SUCCESS;;
 }
 
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
@@ -2249,113 +2272,125 @@ static enum libusb_log_level get_env_debug_level(void)
  * context will be created. If there was already a default context, it will
  * be reused (and nothing will be initialized/reinitialized).
  *
- * \param context Optional output location for context pointer.
+ * \param ctx Optional output location for context pointer.
  * Only valid on return code 0.
  * \returns 0 on success, or a LIBUSB_ERROR code on failure
  * \see libusb_contexts
  */
-int API_EXPORTED libusb_init(libusb_context **context)
+int API_EXPORTED libusb_init(libusb_context **ctx)
 {
-       struct libusb_device *dev, *next;
        size_t priv_size = usbi_backend.context_priv_size;
-       struct libusb_context *ctx;
-       static int first_init = 1;
-       int r = 0;
+       struct libusb_context *_ctx;
+       int r;
 
        usbi_mutex_static_lock(&default_context_lock);
 
-       if (!timestamp_origin.tv_sec)
-               usbi_get_monotonic_time(&timestamp_origin);
-
-       if (!context && usbi_default_context) {
-               usbi_dbg("reusing default context");
+       if (!ctx && default_context_refcnt > 0) {
+               usbi_dbg(usbi_default_context, "reusing default context");
                default_context_refcnt++;
                usbi_mutex_static_unlock(&default_context_lock);
                return 0;
        }
 
-       ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
-       if (!ctx) {
-               r = LIBUSB_ERROR_NO_MEM;
-               goto err_unlock;
+       /* check for first init */
+       if (!active_contexts_list.next) {
+               list_init(&active_contexts_list);
+               usbi_get_monotonic_time(&timestamp_origin);
+       }
+
+       _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size);
+       if (!_ctx) {
+               usbi_mutex_static_unlock(&default_context_lock);
+               return LIBUSB_ERROR_NO_MEM;
        }
 
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
-       ctx->debug = get_env_debug_level();
-       if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
-               ctx->debug_fixed = 1;
+       if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
+               _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
+       } else {
+               _ctx->debug = get_env_debug_level();
+       }
+       if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
+               _ctx->debug_fixed = 1;
 #endif
 
-       /* default context should be initialized before calling usbi_dbg */
-       if (!usbi_default_context) {
-               usbi_default_context = ctx;
-               default_context_refcnt++;
-               usbi_dbg("created default context");
+       usbi_mutex_init(&_ctx->usb_devs_lock);
+       usbi_mutex_init(&_ctx->open_devs_lock);
+       list_init(&_ctx->usb_devs);
+       list_init(&_ctx->open_devs);
+
+       /* apply default options to all new contexts */
+       for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) {
+               if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) {
+                       continue;
+               }
+               r = libusb_set_option(_ctx, option);
+               if (LIBUSB_SUCCESS != r)
+                       goto err_free_ctx;
+       }
+
+       /* default context must be initialized before calling usbi_dbg */
+       if (!ctx) {
+               usbi_default_context = _ctx;
+               default_context_refcnt = 1;
+               usbi_dbg(usbi_default_context, "created default context");
        }
 
-       usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
+       usbi_dbg(_ctx, "libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
                libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc);
 
-       usbi_mutex_init(&ctx->usb_devs_lock);
-       usbi_mutex_init(&ctx->open_devs_lock);
-       usbi_mutex_init(&ctx->hotplug_cbs_lock);
-       list_init(&ctx->usb_devs);
-       list_init(&ctx->open_devs);
-       list_init(&ctx->hotplug_cbs);
-       ctx->next_hotplug_cb_handle = 1;
+       r = usbi_io_init(_ctx);
+       if (r < 0)
+               goto err_free_ctx;
 
        usbi_mutex_static_lock(&active_contexts_lock);
-       if (first_init) {
-               first_init = 0;
-               list_init(&active_contexts_list);
-       }
-       list_add (&ctx->list, &active_contexts_list);
+       list_add(&_ctx->list, &active_contexts_list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
        if (usbi_backend.init) {
-               r = usbi_backend.init(ctx);
+               r = usbi_backend.init(_ctx);
                if (r)
-                       goto err_free_ctx;
+                       goto err_io_exit;
        }
 
-       r = usbi_io_init(ctx);
-       if (r < 0)
-               goto err_backend_exit;
+       /* Initialize hotplug after the initial enumeration is done. */
+       usbi_hotplug_init(_ctx);
 
-       usbi_mutex_static_unlock(&default_context_lock);
+       if (ctx) {
+               *ctx = _ctx;
 
-       if (context)
-               *context = ctx;
+               if (!usbi_fallback_context) {
+                       usbi_fallback_context = _ctx;
+                       usbi_warn(usbi_fallback_context, "installing new context as implicit default");
+               }
+       }
 
-       return 0;
+       usbi_mutex_static_unlock(&default_context_lock);
 
-err_backend_exit:
-       if (usbi_backend.exit)
-               usbi_backend.exit(ctx);
-err_free_ctx:
-       if (ctx == usbi_default_context) {
-               usbi_default_context = NULL;
-               default_context_refcnt--;
-       }
+       return 0;
 
+err_io_exit:
        usbi_mutex_static_lock(&active_contexts_lock);
-       list_del(&ctx->list);
+       list_del(&_ctx->list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
-       usbi_mutex_lock(&ctx->usb_devs_lock);
-       for_each_device_safe(ctx, dev, next) {
-               list_del(&dev->list);
-               libusb_unref_device(dev);
+       usbi_hotplug_exit(_ctx);
+       usbi_io_exit(_ctx);
+
+err_free_ctx:
+       if (!ctx) {
+               /* clear default context that was not fully initialized */
+               usbi_default_context = NULL;
+               default_context_refcnt = 0;
        }
-       usbi_mutex_unlock(&ctx->usb_devs_lock);
 
-       usbi_mutex_destroy(&ctx->open_devs_lock);
-       usbi_mutex_destroy(&ctx->usb_devs_lock);
-       usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+       usbi_mutex_destroy(&_ctx->open_devs_lock);
+       usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+       free(_ctx);
 
-       free(ctx);
-err_unlock:
        usbi_mutex_static_unlock(&default_context_lock);
+
        return r;
 }
 
@@ -2366,90 +2401,66 @@ err_unlock:
  */
 void API_EXPORTED libusb_exit(libusb_context *ctx)
 {
-       struct libusb_device *dev, *next;
-       struct timeval tv = { 0, 0 };
-       int destroying_default_context = 0;
-
-       usbi_dbg(" ");
+       struct libusb_context *_ctx;
+       struct libusb_device *dev;
 
-       ctx = usbi_get_context(ctx);
+       usbi_mutex_static_lock(&default_context_lock);
 
        /* if working with default context, only actually do the deinitialization
         * if we're the last user */
-       usbi_mutex_static_lock(&default_context_lock);
-       if (ctx == usbi_default_context) {
+       if (!ctx) {
                if (!usbi_default_context) {
-                       usbi_dbg("no default context, not initialized?");
+                       usbi_dbg(ctx, "no default context, not initialized?");
                        usbi_mutex_static_unlock(&default_context_lock);
                        return;
                }
 
                if (--default_context_refcnt > 0) {
-                       usbi_dbg("not destroying default context");
+                       usbi_dbg(ctx, "not destroying default context");
                        usbi_mutex_static_unlock(&default_context_lock);
                        return;
                }
-               usbi_dbg("destroying default context");
 
-               /*
-                * Setting this flag without unlocking the default context, as
-                * we are actually destroying the default context.
-                * usbi_default_context is not set to NULL yet, as all activities
-                * would only stop after usbi_backend->exit() returns.
-                */
-               destroying_default_context = 1;
+               usbi_dbg(ctx, "destroying default context");
+               _ctx = usbi_default_context;
        } else {
-               /* Unlock default context, as we're not modifying it. */
-               usbi_mutex_static_unlock(&default_context_lock);
+               usbi_dbg(ctx, " ");
+               _ctx = ctx;
        }
 
        usbi_mutex_static_lock(&active_contexts_lock);
-       list_del(&ctx->list);
+       list_del(&_ctx->list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
-       if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
-               usbi_hotplug_deregister(ctx, 1);
-
-               /*
-                * Ensure any pending unplug events are read from the hotplug
-                * pipe. The usb_device-s hold in the events are no longer part
-                * of usb_devs, but the events still hold a reference!
-                *
-                * Note we don't do this if the application has left devices
-                * open (which implies a buggy app) to avoid packet completion
-                * handlers running when the app does not expect them to run.
-                */
-               if (list_empty(&ctx->open_devs))
-                       libusb_handle_events_timeout(ctx, &tv);
+       if (usbi_backend.exit)
+               usbi_backend.exit(_ctx);
 
-               usbi_mutex_lock(&ctx->usb_devs_lock);
-               for_each_device_safe(ctx, dev, next) {
-                       list_del(&dev->list);
-                       libusb_unref_device(dev);
-               }
-               usbi_mutex_unlock(&ctx->usb_devs_lock);
-       }
+       if (!ctx)
+               usbi_default_context = NULL;
+       if (ctx == usbi_fallback_context)
+               usbi_fallback_context = NULL;
 
-       /* a few sanity checks. don't bother with locking because unless
-        * there is an application bug, nobody will be accessing these. */
-       if (!list_empty(&ctx->usb_devs))
-               usbi_warn(ctx, "some libusb_devices were leaked");
-       if (!list_empty(&ctx->open_devs))
-               usbi_warn(ctx, "application left some devices open");
+       usbi_mutex_static_unlock(&default_context_lock);
 
-       usbi_io_exit(ctx);
-       if (usbi_backend.exit)
-               usbi_backend.exit(ctx);
+       /* Don't bother with locking after this point because unless there is
+        * an application bug, nobody will be accessing the context. */
 
-       usbi_mutex_destroy(&ctx->open_devs_lock);
-       usbi_mutex_destroy(&ctx->usb_devs_lock);
-       usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
-       free(ctx);
+       usbi_hotplug_exit(_ctx);
+       usbi_io_exit(_ctx);
 
-       if (destroying_default_context) {
-               usbi_default_context = NULL;
-               usbi_mutex_static_unlock(&default_context_lock);
+       for_each_device(_ctx, dev) {
+               usbi_warn(_ctx, "device %d.%d still referenced",
+                       dev->bus_number, dev->device_address);
+               DEVICE_CTX(dev) = NULL;
        }
+
+       if (!list_empty(&_ctx->open_devs))
+               usbi_warn(_ctx, "application left some devices open");
+
+       usbi_mutex_destroy(&_ctx->open_devs_lock);
+       usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+       free(_ctx);
 }
 
 /** \ingroup libusb_misc
@@ -2574,7 +2585,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level,
 #else
        enum libusb_log_level ctx_level;
 
-       ctx = usbi_get_context(ctx);
+       ctx = ctx ? ctx : usbi_default_context;
+       ctx = ctx ? ctx : usbi_fallback_context;
        if (ctx)
                ctx_level = ctx->debug;
        else
index ecd9441804e6b3692dc25933f618592429e7384e..253ef1c319ef48f3a7fdbfe7820ff01e3105d376 100644 (file)
@@ -140,7 +140,7 @@ static int parse_endpoint(struct libusb_context *ctx,
                    header->bDescriptorType == LIBUSB_DT_DEVICE)
                        break;
 
-               usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+               usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
                buffer += header->bLength;
                size -= header->bLength;
                parsed += header->bLength;
@@ -414,7 +414,7 @@ static int parse_configuration(struct libusb_context *ctx,
                            header->bDescriptorType == LIBUSB_DT_DEVICE)
                                break;
 
-                       usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+                       usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
                        buffer += header->bLength;
                        size -= header->bLength;
                }
@@ -531,7 +531,7 @@ static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx,
 int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
        struct libusb_device_descriptor *desc)
 {
-       usbi_dbg(" ");
+       usbi_dbg(DEVICE_CTX(dev), " ");
        static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE,
                      "struct libusb_device_descriptor is not expected size");
        *desc = dev->device_descriptor;
@@ -601,7 +601,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
        uint8_t *buf;
        int r;
 
-       usbi_dbg("index %u", config_index);
+       usbi_dbg(DEVICE_CTX(dev), "index %u", config_index);
        if (config_index >= dev->device_descriptor.bNumConfigurations)
                return LIBUSB_ERROR_NOT_FOUND;
 
@@ -656,7 +656,7 @@ int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
                return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
        }
 
-       usbi_dbg("value %u", bConfigurationValue);
+       usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue);
        for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
                union usbi_config_desc_buf _config;
 
@@ -850,23 +850,24 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
        uint16_t bos_len;
        uint8_t *bos_data;
        int r;
+       struct libusb_context *ctx = HANDLE_CTX(dev_handle);
 
        /* Read the BOS. This generates 2 requests on the bus,
         * one for the header, and one for the full BOS */
        r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf));
        if (r < 0) {
                if (r != LIBUSB_ERROR_PIPE)
-                       usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+                       usbi_err(ctx, "failed to read BOS (%d)", r);
                return r;
        }
        if (r < LIBUSB_DT_BOS_SIZE) {
-               usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d",
+               usbi_err(ctx, "short BOS read %d/%d",
                         r, LIBUSB_DT_BOS_SIZE);
                return LIBUSB_ERROR_IO;
        }
 
        bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength);
-       usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities",
+       usbi_dbg(ctx, "found BOS descriptor: size %u bytes, %u capabilities",
                 bos_len, _bos.desc.bNumDeviceCaps);
        bos_data = calloc(1, bos_len);
        if (!bos_data)
@@ -875,11 +876,10 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
        r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len);
        if (r >= 0) {
                if (r != (int)bos_len)
-                       usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u",
-                                 r, bos_len);
+                       usbi_warn(ctx, "short BOS read %d/%u", r, bos_len);
                r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r);
        } else {
-               usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+               usbi_err(ctx, "failed to read BOS (%d)", r);
        }
 
        free(bos_data);
@@ -1109,7 +1109,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
        else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
                return LIBUSB_ERROR_IO;
        else if (str.desc.bLength & 1)
-               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength);
 
        langid = libusb_le16_to_cpu(str.desc.wData[0]);
        r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf));
@@ -1120,7 +1120,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
        else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
                return LIBUSB_ERROR_IO;
        else if ((str.desc.bLength & 1) || str.desc.bLength != r)
-               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r);
 
        di = 0;
        for (si = 2; si < str.desc.bLength; si += 2) {
index e3e5e76e688d5772e5aca480a602bcd3401cf4a9..6b743c70442e42080338bf92b8ee21630522ca35 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
 /*
  * Hotplug functions for libusb
- * Copyright Â© 2012-2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright Â© 2012-2021 Nathan Hjelm <hjelmn@mac.com>
  * Copyright Â© 2012-2013 Peter Stuge <peter@stuge.se>
  *
  * This library is free software; you can redistribute it and/or
@@ -20,7 +20,6 @@
  */
 
 #include "libusbi.h"
-#include "hotplug.h"
 
 /**
  * @defgroup libusb_hotplug Device hotplug event notification
@@ -63,7 +62,7 @@
  * A hotplug event can listen for either or both of these events.
  *
  * Note: If you receive notification that a device has left and you have any
- * libusb_device_handles for the device it is up to you to call libusb_close()
+ * libusb_device_handles for the device it is up to you to call libusb_close()
  * on each device handle to free up any remaining resources associated with the device.
  * Once a device has left any libusb_device_handle associated with the device
  * are invalid and will remain so even if the device comes back.
@@ -144,15 +143,79 @@ int main (void) {
  */
 
 #define VALID_HOTPLUG_EVENTS                   \
-        (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
-         LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+       (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |  \
+        LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
 
 #define VALID_HOTPLUG_FLAGS                    \
-        (LIBUSB_HOTPLUG_ENUMERATE)
+       (LIBUSB_HOTPLUG_ENUMERATE)
 
-static int usbi_hotplug_match_cb(struct libusb_context *ctx,
-       struct libusb_device *dev, libusb_hotplug_event event,
-       struct libusb_hotplug_callback *hotplug_cb)
+void usbi_hotplug_init(struct libusb_context *ctx)
+{
+       /* check for hotplug support */
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+               return;
+
+       usbi_mutex_init(&ctx->hotplug_cbs_lock);
+       list_init(&ctx->hotplug_cbs);
+       ctx->next_hotplug_cb_handle = 1;
+       usbi_atomic_store(&ctx->hotplug_ready, 1);
+}
+
+void usbi_hotplug_exit(struct libusb_context *ctx)
+{
+       struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+       struct usbi_hotplug_message *msg;
+       struct libusb_device *dev, *next_dev;
+
+       /* check for hotplug support */
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+               return;
+
+       if (!usbi_atomic_load(&ctx->hotplug_ready))
+               return;
+
+       /* free all registered hotplug callbacks */
+       for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+               list_del(&hotplug_cb->list);
+               free(hotplug_cb);
+       }
+
+       /* free all pending hotplug messages */
+       while (!list_empty(&ctx->hotplug_msgs)) {
+               msg = list_first_entry(&ctx->hotplug_msgs, struct usbi_hotplug_message, list);
+
+               /* if the device left, the message holds a reference
+                * and we must drop it */
+               if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+                       libusb_unref_device(msg->device);
+
+               list_del(&msg->list);
+               free(msg);
+       }
+
+       /* free all discovered devices. due to parent references loop until no devices are freed. */
+       for_each_device_safe(ctx, dev, next_dev) {
+               /* remove the device from the usb_devs list only if there are no
+                * references held, otherwise leave it on the list so that a
+                * warning message will be shown */
+               if (usbi_atomic_load(&dev->refcnt) == 1) {
+                       list_del(&dev->list);
+               }
+               if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
+                       /* the parent was before this device in the list and will be released.
+                          remove it from the list. this is safe as parent_dev can not be
+                          equal to next_dev. */
+                       assert (dev->parent_dev != next_dev);
+                       list_del(&dev->parent_dev->list);
+               }
+               libusb_unref_device(dev);
+       }
+
+       usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+}
+
+static int usbi_hotplug_match_cb(struct libusb_device *dev,
+       libusb_hotplug_event event, struct usbi_hotplug_callback *hotplug_cb)
 {
        if (!(hotplug_cb->flags & event)) {
                return 0;
@@ -173,71 +236,100 @@ static int usbi_hotplug_match_cb(struct libusb_context *ctx,
                return 0;
        }
 
-       return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data);
-}
-
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
-       libusb_hotplug_event event)
-{
-       struct libusb_hotplug_callback *hotplug_cb, *next;
-       int ret;
-
-       usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-
-       for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
-               if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
-                       /* process deregistration in usbi_hotplug_deregister() */
-                       continue;
-               }
-
-               usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-               ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb);
-               usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-
-               if (ret) {
-                       list_del(&hotplug_cb->list);
-                       free(hotplug_cb);
-               }
-       }
-
-       usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+       return hotplug_cb->cb(DEVICE_CTX(dev), dev, event, hotplug_cb->user_data);
 }
 
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
        libusb_hotplug_event event)
 {
-       struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+       struct usbi_hotplug_message *msg;
        unsigned int event_flags;
 
-       if (!message) {
+       /* Only generate a notification if hotplug is ready. This prevents hotplug
+        * notifications from being generated during initial enumeration or if the
+        * backend does not support hotplug. */
+       if (!usbi_atomic_load(&ctx->hotplug_ready))
+               return;
+
+       msg = calloc(1, sizeof(*msg));
+       if (!msg) {
                usbi_err(ctx, "error allocating hotplug message");
                return;
        }
 
-       message->event = event;
-       message->device = dev;
+       msg->event = event;
+       msg->device = dev;
 
        /* Take the event data lock and add this message to the list.
         * Only signal an event if there are no prior pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
        event_flags = ctx->event_flags;
        ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
-       list_add_tail(&message->list, &ctx->hotplug_msgs);
+       list_add_tail(&msg->list, &ctx->hotplug_msgs);
        if (!event_flags)
                usbi_signal_event(&ctx->event);
        usbi_mutex_unlock(&ctx->event_data_lock);
 }
 
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs)
+{
+       struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+       struct usbi_hotplug_message *msg;
+       int r;
+
+       usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+       /* dispatch all pending hotplug messages */
+       while (!list_empty(hotplug_msgs)) {
+               msg = list_first_entry(hotplug_msgs, struct usbi_hotplug_message, list);
+
+               for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+                       /* skip callbacks that have unregistered */
+                       if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)
+                               continue;
+
+                       usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+                       r = usbi_hotplug_match_cb(msg->device, msg->event, hotplug_cb);
+                       usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+                       if (r) {
+                               list_del(&hotplug_cb->list);
+                               free(hotplug_cb);
+                       }
+               }
+
+               /* if the device left, the message holds a reference
+                * and we must drop it */
+               if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+                       libusb_unref_device(msg->device);
+
+               list_del(&msg->list);
+               free(msg);
+       }
+
+       /* free any callbacks that have unregistered */
+       for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+               if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
+                       usbi_dbg(ctx, "freeing hotplug cb %p with handle %d",
+                               hotplug_cb, hotplug_cb->handle);
+                       list_del(&hotplug_cb->list);
+                       free(hotplug_cb);
+               }
+       }
+
+       usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+}
+
 int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
        int events, int flags,
        int vendor_id, int product_id, int dev_class,
        libusb_hotplug_callback_fn cb_fn, void *user_data,
        libusb_hotplug_callback_handle *callback_handle)
 {
-       struct libusb_hotplug_callback *new_callback;
+       struct usbi_hotplug_callback *hotplug_cb;
 
        /* check for sane values */
-       if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
+       if (!events || (~VALID_HOTPLUG_EVENTS & events) ||
            (~VALID_HOTPLUG_FLAGS & flags) ||
            (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
            (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
@@ -247,47 +339,45 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
        }
 
        /* check for hotplug support */
-       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
                return LIBUSB_ERROR_NOT_SUPPORTED;
-       }
 
        ctx = usbi_get_context(ctx);
 
-       new_callback = calloc(1, sizeof(*new_callback));
-       if (!new_callback) {
+       hotplug_cb = calloc(1, sizeof(*hotplug_cb));
+       if (!hotplug_cb)
                return LIBUSB_ERROR_NO_MEM;
-       }
 
-       new_callback->flags = (uint8_t)events;
+       hotplug_cb->flags = (uint8_t)events;
        if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
-               new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
-               new_callback->vendor_id = (uint16_t)vendor_id;
+               hotplug_cb->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
+               hotplug_cb->vendor_id = (uint16_t)vendor_id;
        }
        if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
-               new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
-               new_callback->product_id = (uint16_t)product_id;
+               hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
+               hotplug_cb->product_id = (uint16_t)product_id;
        }
        if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
-               new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
-               new_callback->dev_class = (uint8_t)dev_class;
+               hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
+               hotplug_cb->dev_class = (uint8_t)dev_class;
        }
-       new_callback->cb = cb_fn;
-       new_callback->user_data = user_data;
+       hotplug_cb->cb = cb_fn;
+       hotplug_cb->user_data = user_data;
 
        usbi_mutex_lock(&ctx->hotplug_cbs_lock);
 
        /* protect the handle by the context hotplug lock */
-       new_callback->handle = ctx->next_hotplug_cb_handle++;
+       hotplug_cb->handle = ctx->next_hotplug_cb_handle++;
 
        /* handle the unlikely case of overflow */
        if (ctx->next_hotplug_cb_handle < 0)
                ctx->next_hotplug_cb_handle = 1;
 
-       list_add(&new_callback->list, &ctx->hotplug_cbs);
+       list_add(&hotplug_cb->list, &ctx->hotplug_cbs);
 
        usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
-       usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
+       usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle);
 
        if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
                ssize_t i, len;
@@ -295,23 +385,21 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
 
                len = libusb_get_device_list(ctx, &devs);
                if (len < 0) {
-                       libusb_hotplug_deregister_callback(ctx,
-                                                       new_callback->handle);
+                       libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle);
                        return (int)len;
                }
 
                for (i = 0; i < len; i++) {
-                       usbi_hotplug_match_cb(ctx, devs[i],
+                       usbi_hotplug_match_cb(devs[i],
                                        LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
-                                       new_callback);
+                                       hotplug_cb);
                }
 
                libusb_free_device_list(devs, 1);
        }
 
-
        if (callback_handle)
-               *callback_handle = new_callback->handle;
+               *callback_handle = hotplug_cb->handle;
 
        return LIBUSB_SUCCESS;
 }
@@ -319,24 +407,24 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
 void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
        libusb_hotplug_callback_handle callback_handle)
 {
-       struct libusb_hotplug_callback *hotplug_cb;
+       struct usbi_hotplug_callback *hotplug_cb;
        int deregistered = 0;
 
        /* check for hotplug support */
-       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
                return;
-       }
 
-       usbi_dbg("deregister hotplug cb %d", callback_handle);
+       usbi_dbg(ctx, "deregister hotplug cb %d", callback_handle);
 
        ctx = usbi_get_context(ctx);
 
        usbi_mutex_lock(&ctx->hotplug_cbs_lock);
        for_each_hotplug_cb(ctx, hotplug_cb) {
                if (callback_handle == hotplug_cb->handle) {
-                       /* Mark this callback for deregistration */
+                       /* mark this callback for deregistration */
                        hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
                        deregistered = 1;
+                       break;
                }
        }
        usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
@@ -357,15 +445,14 @@ DEFAULT_VISIBILITY
 void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
        libusb_hotplug_callback_handle callback_handle)
 {
-       struct libusb_hotplug_callback *hotplug_cb;
+       struct usbi_hotplug_callback *hotplug_cb;
        void *user_data = NULL;
 
        /* check for hotplug support */
-       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
                return NULL;
-       }
 
-       usbi_dbg("get hotplug user data %d", callback_handle);
+       usbi_dbg(ctx, "get hotplug cb %d user data", callback_handle);
 
        ctx = usbi_get_context(ctx);
 
@@ -373,25 +460,10 @@ void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
        for_each_hotplug_cb(ctx, hotplug_cb) {
                if (callback_handle == hotplug_cb->handle) {
                        user_data = hotplug_cb->user_data;
+                       break;
                }
        }
        usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
        return user_data;
 }
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
-{
-       struct libusb_hotplug_callback *hotplug_cb, *next;
-
-       usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-       for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
-               if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
-                       usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
-                                hotplug_cb->handle);
-                       list_del(&hotplug_cb->list);
-                       free(hotplug_cb);
-               }
-       }
-       usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-}
diff --git a/mac/libusb/hotplug.h b/mac/libusb/hotplug.h
deleted file mode 100644 (file)
index 161f7e5..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
-/*
- * Hotplug support for libusb
- * Copyright Â© 2012-2013 Nathan Hjelm <hjelmn@mac.com>
- * Copyright Â© 2012-2013 Peter Stuge <peter@stuge.se>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef USBI_HOTPLUG_H
-#define USBI_HOTPLUG_H
-
-#include "libusbi.h"
-
-enum usbi_hotplug_flags {
-       /* This callback is interested in device arrivals */
-       USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
-
-       /* This callback is interested in device removals */
-       USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
-
-       /* IMPORTANT: The values for the below entries must start *after*
-        * the highest value of the above entries!!!
-        */
-
-       /* The vendor_id field is valid for matching */
-       USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
-
-       /* The product_id field is valid for matching */
-       USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
-
-       /* The dev_class field is valid for matching */
-       USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
-
-       /* This callback has been unregistered and needs to be freed */
-       USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
-};
-
-/** \ingroup hotplug
- * The hotplug callback structure. The user populates this structure with
- * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
- * to receive notification of hotplug events.
- */
-struct libusb_hotplug_callback {
-       /** Flags that control how this callback behaves */
-       uint8_t flags;
-
-       /** Vendor ID to match (if flags says this is valid) */
-       uint16_t vendor_id;
-
-       /** Product ID to match (if flags says this is valid) */
-       uint16_t product_id;
-
-       /** Device class to match (if flags says this is valid) */
-       uint8_t dev_class;
-
-       /** Callback function to invoke for matching event/device */
-       libusb_hotplug_callback_fn cb;
-
-       /** Handle for this callback (used to match on deregister) */
-       libusb_hotplug_callback_handle handle;
-
-       /** User data that will be passed to the callback function */
-       void *user_data;
-
-       /** List this callback is registered in (ctx->hotplug_cbs) */
-       struct list_head list;
-};
-
-struct libusb_hotplug_message {
-       /** The hotplug event that occurred */
-       libusb_hotplug_event event;
-
-       /** The device for which this hotplug event occurred */
-       struct libusb_device *device;
-
-       /** List this message is contained in (ctx->hotplug_msgs) */
-       struct list_head list;
-};
-
-#define for_each_hotplug_cb(ctx, c) \
-       for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-#define for_each_hotplug_cb_safe(ctx, c, n) \
-       for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
-       libusb_hotplug_event event);
-void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
-       libusb_hotplug_event event);
-
-#endif
index 0e960ddfddd5292db40d8829084079eb081ad69d..9e3146cf9fdb31223a3cd27f7998e797e7853bd6 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include "libusbi.h"
-#include "hotplug.h"
 
 /**
  * \page libusb_io Synchronous and asynchronous device I/O
@@ -73,7 +72,7 @@
  * a single function call. When the function call returns, the transfer has
  * completed and you can parse the results.
  *
- * If you have used the libusb-0.1 before, this I/O style will seem familiar to
+ * If you have used libusb-0.1 before, this I/O style will seem familiar to
  * you. libusb-0.1 only offered a synchronous interface.
  *
  * In our input device example, to read button presses you might write code
@@ -1181,12 +1180,12 @@ int usbi_io_init(struct libusb_context *ctx)
 #ifdef HAVE_OS_TIMER
        r = usbi_create_timer(&ctx->timer);
        if (r == 0) {
-               usbi_dbg("using timer for timeouts");
+               usbi_dbg(ctx, "using timer for timeouts");
                r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
                if (r < 0)
                        goto err_destroy_timer;
        } else {
-               usbi_dbg("timer not available for timeouts");
+               usbi_dbg(ctx, "timer not available for timeouts");
        }
 #endif
 
@@ -1310,7 +1309,6 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
        itransfer->priv = ptr;
        usbi_mutex_init(&itransfer->lock);
        transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       usbi_dbg("transfer %p", transfer);
        return transfer;
 }
 
@@ -1340,12 +1338,14 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
        if (!transfer)
                return;
 
-       usbi_dbg("transfer %p", transfer);
+       usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer);
        if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
                free(transfer->buffer);
 
        itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
        usbi_mutex_destroy(&itransfer->lock);
+       if (itransfer->dev)
+               libusb_unref_device(itransfer->dev);
 
        priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
        ptr = (unsigned char *)itransfer - priv_size;
@@ -1376,12 +1376,12 @@ static int arm_timer_for_next_timeout(struct libusb_context *ctx)
 
                /* act on first transfer that has not already been handled */
                if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
-                       usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+                       usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
                        return usbi_arm_timer(&ctx->timer, cur_ts);
                }
        }
 
-       usbi_dbg("no timeouts, disarming timer");
+       usbi_dbg(ctx, "no timeouts, disarming timer");
        return usbi_disarm_timer(&ctx->timer);
 }
 #else
@@ -1438,7 +1438,7 @@ out:
        if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
                /* if this transfer has the lowest timeout of all active transfers,
                 * rearm the timer with this transfer's timeout */
-               usbi_dbg("arm timer for timeout in %ums (first in line)",
+               usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)",
                        USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
                r = usbi_arm_timer(&ctx->timer, timeout);
        }
@@ -1491,10 +1491,16 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
 {
        struct usbi_transfer *itransfer =
                LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
-       struct libusb_context *ctx = TRANSFER_CTX(transfer);
+       struct libusb_context *ctx;
        int r;
 
-       usbi_dbg("transfer %p", transfer);
+       assert(transfer->dev_handle);
+       if (itransfer->dev)
+               libusb_unref_device(itransfer->dev);
+       itransfer->dev = libusb_ref_device(transfer->dev_handle->dev);
+
+       ctx = HANDLE_CTX(transfer->dev_handle);
+       usbi_dbg(ctx, "transfer %p", transfer);
 
        /*
         * Important note on locking, this function takes / releases locks
@@ -1546,15 +1552,13 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
        }
        /*
         * We must release the flying transfers lock here, because with
-        * some backends the submit_transfer method is synchroneous.
+        * some backends the submit_transfer method is synchronous.
         */
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
        r = usbi_backend.submit_transfer(itransfer);
        if (r == LIBUSB_SUCCESS) {
                itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
-               /* keep a reference to this device */
-               libusb_ref_device(transfer->dev_handle->dev);
        }
        usbi_mutex_unlock(&itransfer->lock);
 
@@ -1572,6 +1576,26 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
  * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
  * "LIBUSB_TRANSFER_CANCELLED."
  *
+ * This function behaves differently on Darwin-based systems (macOS and iOS):
+ *
+ * - Calling this function for one transfer will cause all transfers on the
+ *   same endpoint to be cancelled. Your callback function will be invoked with
+ *   a transfer status of
+ *   \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
+ *   "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled.
+
+ * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request
+ *   for the transfer's endpoint. If the device does not handle this request
+ *   correctly, the data toggle bits for the endpoint can be left out of sync
+ *   between host and device, which can have unpredictable results when the
+ *   next data is sent on the endpoint, including data being silently lost.
+ *   A call to \ref libusb_clear_halt will not resolve this situation, since
+ *   that function uses the same request. Therefore, if your program runs on
+ *   Darwin and uses a device that does not correctly implement
+ *   \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel
+ *   transfers when followed by a device reset using
+ *   \ref libusb_reset_device.
+ *
  * \param transfer the transfer to cancel
  * \returns 0 on success
  * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
@@ -1582,9 +1606,10 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
 {
        struct usbi_transfer *itransfer =
                LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
        int r;
 
-       usbi_dbg("transfer %p", transfer );
+       usbi_dbg(ctx, "transfer %p", transfer );
        usbi_mutex_lock(&itransfer->lock);
        if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
                        || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
@@ -1595,10 +1620,9 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
        if (r < 0) {
                if (r != LIBUSB_ERROR_NOT_FOUND &&
                    r != LIBUSB_ERROR_NO_DEVICE)
-                       usbi_err(TRANSFER_CTX(transfer),
-                               "cancel transfer failed error %d", r);
+                       usbi_err(ctx, "cancel transfer failed error %d", r);
                else
-                       usbi_dbg("cancel transfer failed error %d", r);
+                       usbi_dbg(ctx, "cancel transfer failed error %d", r);
 
                if (r == LIBUSB_ERROR_NO_DEVICE)
                        itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
@@ -1661,13 +1685,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
 {
        struct libusb_transfer *transfer =
                USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       struct libusb_device_handle *dev_handle = transfer->dev_handle;
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
        uint8_t flags;
        int r;
 
        r = remove_from_flying_list(itransfer);
        if (r < 0)
-               usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
+               usbi_err(ctx, "failed to set timer for next timeout");
 
        usbi_mutex_lock(&itransfer->lock);
        itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
@@ -1679,7 +1703,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
                if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
                        rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
                if (rqlen != itransfer->transferred) {
-                       usbi_dbg("interpreting short transfer as error");
+                       usbi_dbg(ctx, "interpreting short transfer as error");
                        status = LIBUSB_TRANSFER_ERROR;
                }
        }
@@ -1687,14 +1711,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
        flags = transfer->flags;
        transfer->status = status;
        transfer->actual_length = itransfer->transferred;
-       usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+       usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback);
        if (transfer->callback)
                transfer->callback(transfer);
        /* transfer might have been freed by the above call, do not use from
         * this point. */
        if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
                libusb_free_transfer(transfer);
-       libusb_unref_device(dev_handle->dev);
        return r;
 }
 
@@ -1715,7 +1738,7 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
 
        /* if the URB was cancelled due to timeout, report timeout to the user */
        if (timed_out) {
-               usbi_dbg("detected timeout cancellation");
+               usbi_dbg(ctx, "detected timeout cancellation");
                return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
        }
 
@@ -1728,10 +1751,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
  * function will be called the next time an event handler runs. */
 void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
 {
-       libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle;
+       struct libusb_device *dev = itransfer->dev;
 
-       if (dev_handle) {
-               struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+       if (dev) {
+               struct libusb_context *ctx = DEVICE_CTX(dev);
                unsigned int event_flags;
 
                usbi_mutex_lock(&ctx->event_data_lock);
@@ -1776,7 +1799,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
        ru = ctx->device_close;
        usbi_mutex_unlock(&ctx->event_data_lock);
        if (ru) {
-               usbi_dbg("someone else is closing a device");
+               usbi_dbg(ctx, "someone else is closing a device");
                return 1;
        }
 
@@ -1868,7 +1891,7 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
        r = ctx->device_close;
        usbi_mutex_unlock(&ctx->event_data_lock);
        if (r) {
-               usbi_dbg("someone else is closing a device");
+               usbi_dbg(ctx, "someone else is closing a device");
                return 0;
        }
 
@@ -1897,7 +1920,7 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
        r = ctx->device_close;
        usbi_mutex_unlock(&ctx->event_data_lock);
        if (r) {
-               usbi_dbg("someone else is closing a device");
+               usbi_dbg(ctx, "someone else is closing a device");
                return 1;
        }
 
@@ -1918,7 +1941,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
 {
        unsigned int event_flags;
 
-       usbi_dbg(" ");
+       usbi_dbg(ctx, " ");
 
        ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->event_data_lock);
@@ -2073,9 +2096,10 @@ static void handle_timeouts(struct libusb_context *ctx)
 static int handle_event_trigger(struct libusb_context *ctx)
 {
        struct list_head hotplug_msgs;
+       int hotplug_event = 0;
        int r = 0;
 
-       usbi_dbg("event triggered");
+       usbi_dbg(ctx, "event triggered");
 
        list_init(&hotplug_msgs);
 
@@ -2084,21 +2108,28 @@ static int handle_event_trigger(struct libusb_context *ctx)
 
        /* check if someone modified the event sources */
        if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
-               usbi_dbg("someone updated the event sources");
+               usbi_dbg(ctx, "someone updated the event sources");
 
        if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
-               usbi_dbg("someone purposefully interrupted");
+               usbi_dbg(ctx, "someone purposefully interrupted");
                ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
        }
 
+       if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
+               usbi_dbg(ctx, "someone unregistered a hotplug cb");
+               ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+               hotplug_event = 1;
+       }
+
        /* check if someone is closing a device */
        if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
-               usbi_dbg("someone is closing a device");
+               usbi_dbg(ctx, "someone is closing a device");
 
        /* check for any pending hotplug messages */
        if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
-               usbi_dbg("hotplug message received");
+               usbi_dbg(ctx, "hotplug message received");
                ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+               hotplug_event = 1;
                assert(!list_empty(&ctx->hotplug_msgs));
                list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
        }
@@ -2136,20 +2167,9 @@ static int handle_event_trigger(struct libusb_context *ctx)
 
        usbi_mutex_unlock(&ctx->event_data_lock);
 
-       /* process the hotplug messages, if any */
-       while (!list_empty(&hotplug_msgs)) {
-               struct libusb_hotplug_message *message =
-                       list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
-               usbi_hotplug_match(ctx, message->device, message->event);
-
-               /* the device left, dereference the device */
-               if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
-                       libusb_unref_device(message->device);
-
-               list_del(&message->list);
-               free(message);
-       }
+       /* process the hotplug events, if any */
+       if (hotplug_event)
+               usbi_hotplug_process(ctx, &hotplug_msgs);
 
        return r;
 }
@@ -2190,7 +2210,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
         * save the additional overhead */
        usbi_mutex_lock(&ctx->event_data_lock);
        if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
-               usbi_dbg("event sources modified, reallocating event data");
+               usbi_dbg(ctx, "event sources modified, reallocating event data");
 
                /* free anything removed since we last ran */
                cleanup_removed_event_sources(ctx);
@@ -2337,7 +2357,7 @@ retry:
        if (libusb_try_lock_events(ctx) == 0) {
                if (completed == NULL || !*completed) {
                        /* we obtained the event lock: do our own event handling */
-                       usbi_dbg("doing our own event handling");
+                       usbi_dbg(ctx, "doing our own event handling");
                        r = handle_events(ctx, &poll_timeout);
                }
                libusb_unlock_events(ctx);
@@ -2355,11 +2375,11 @@ retry:
                /* we hit a race: whoever was event handling earlier finished in the
                 * time it took us to reach this point. try the cycle again. */
                libusb_unlock_event_waiters(ctx);
-               usbi_dbg("event handler was active but went away, retrying");
+               usbi_dbg(ctx, "event handler was active but went away, retrying");
                goto retry;
        }
 
-       usbi_dbg("another thread is doing event handling");
+       usbi_dbg(ctx, "another thread is doing event handling");
        r = libusb_wait_for_event(ctx, &poll_timeout);
 
 already_done:
@@ -2554,7 +2574,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
        usbi_mutex_lock(&ctx->flying_transfers_lock);
        if (list_empty(&ctx->flying_transfers)) {
                usbi_mutex_unlock(&ctx->flying_transfers_lock);
-               usbi_dbg("no URBs, no timeout!");
+               usbi_dbg(ctx, "no URBs, no timeout!");
                return 0;
        }
 
@@ -2573,19 +2593,19 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
        if (!TIMESPEC_IS_SET(&next_timeout)) {
-               usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+               usbi_dbg(ctx, "no URB with timeout or all handled by OS; no timeout!");
                return 0;
        }
 
        usbi_get_monotonic_time(&systime);
 
        if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
-               usbi_dbg("first timeout already expired");
+               usbi_dbg(ctx, "first timeout already expired");
                timerclear(tv);
        } else {
                TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
                TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
-               usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
+               usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
        }
 
        return 1;
@@ -2656,7 +2676,7 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle
        if (!ievent_source)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+       usbi_dbg(ctx, "add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
        ievent_source->data.os_handle = os_handle;
        ievent_source->data.poll_events = poll_events;
        usbi_mutex_lock(&ctx->event_data_lock);
@@ -2678,7 +2698,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha
        struct usbi_event_source *ievent_source;
        int found = 0;
 
-       usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
+       usbi_dbg(ctx, "remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
        usbi_mutex_lock(&ctx->event_data_lock);
        for_each_event_source(ctx, ievent_source) {
                if (ievent_source->data.os_handle == os_handle) {
@@ -2688,7 +2708,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha
        }
 
        if (!found) {
-               usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
+               usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
                usbi_mutex_unlock(&ctx->event_data_lock);
                return;
        }
@@ -2787,7 +2807,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
        struct usbi_transfer *cur;
        struct usbi_transfer *to_cancel;
 
-       usbi_dbg("device %d.%d",
+       usbi_dbg(ctx, "device %d.%d",
                dev_handle->dev->bus_number, dev_handle->dev->device_address);
 
        /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
@@ -2822,7 +2842,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
                if (!to_cancel)
                        break;
 
-               usbi_dbg("cancelling transfer %p from disconnect",
+               usbi_dbg(ctx, "cancelling transfer %p from disconnect",
                         USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
 
                usbi_mutex_lock(&to_cancel->lock);
index 1308571cd60f352437baf4bcee267a5c01f7b3f6..2592ea779a87121da2d495b424169584811fda22 100644 (file)
 #define LIBUSB_H
 
 #if defined(_MSC_VER)
+#pragma warning(push)
+/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */
+#pragma warning(disable:4200)
 /* on MS environments, the inline keyword is available in C++ only */
 #if !defined(__cplusplus)
 #define inline __inline
 #endif
 /* ssize_t is also not available */
+#ifndef _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED
 #include <basetsd.h>
 typedef SSIZE_T ssize_t;
+#endif /* _SSIZE_T_DEFINED */
 #endif /* _MSC_VER */
 
 #include <limits.h>
@@ -136,7 +142,7 @@ typedef SSIZE_T ssize_t;
  * Internally, LIBUSB_API_VERSION is defined as follows:
  * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
  */
-#define LIBUSB_API_VERSION 0x01000108
+#define LIBUSB_API_VERSION 0x01000109
 
 /* The following is kept for compatibility, but will be deprecated in the future */
 #define LIBUSBX_API_VERSION LIBUSB_API_VERSION
@@ -904,7 +910,7 @@ struct libusb_container_id_descriptor {
 
 /** \ingroup libusb_asyncio
  * Setup packet for control transfers. */
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
 #pragma pack(push, 1)
 #endif
 struct libusb_control_setup {
@@ -932,7 +938,7 @@ struct libusb_control_setup {
        /** Number of bytes to transfer */
        uint16_t wLength;
 } LIBUSB_PACKED;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
 #pragma pack(pop)
 #endif
 
@@ -979,8 +985,9 @@ struct libusb_version {
  * Sessions are created by libusb_init() and destroyed through libusb_exit().
  * If your application is guaranteed to only ever include a single libusb
  * user (i.e. you), you do not have to worry about contexts: pass NULL in
- * every function call where a context is required. The default context
- * will be used.
+ * every function call where a context is required, and the default context
+ * will be used. Note that libusb_set_option(NULL, ...) is special, and adds
+ * an option to a list of default options for new contexts.
  *
  * For more information, see \ref libusb_contexts.
  */
@@ -989,7 +996,7 @@ typedef struct libusb_context libusb_context;
 /** \ingroup libusb_dev
  * Structure representing a USB device detected on the system. This is an
  * opaque type for which you are only ever provided with a pointer, usually
- * originating from libusb_get_device_list().
+ * originating from libusb_get_device_list() or libusb_hotplug_register_callback().
  *
  * Certain operations can be performed on a device, but in order to do any
  * I/O you will have to first obtain a device handle using libusb_open().
@@ -1325,6 +1332,9 @@ enum libusb_log_level {
 
 /** \ingroup libusb_lib
  *  Log callback mode.
+ *
+ *  Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
  * \see libusb_set_log_cb()
  */
 enum libusb_log_cb_mode {
@@ -1341,6 +1351,9 @@ enum libusb_log_cb_mode {
  * is a global log message
  * \param level the log level, see \ref libusb_log_level for a description
  * \param str the log message
+ *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
  * \see libusb_set_log_cb()
  */
 typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
@@ -2092,20 +2105,36 @@ enum libusb_option {
         */
        LIBUSB_OPTION_USE_USBDK = 1,
 
-       /** Set libusb has weak authority. With this option, libusb will skip
-        * scan devices in libusb_init.
+       /** Do not scan for devices
+        *
+        * With this option set, libusb will skip scanning devices in
+        * libusb_init(). Must be set before calling libusb_init().
         *
-        * This option should be set before calling libusb_init(), otherwise
-        * libusb_init will failed. Normally libusb_wrap_sys_device need set
-        * this option.
+        * Hotplug functionality will also be deactivated.
         *
-        * Only valid on Linux-based operating system, such as Android.
+        * The option is useful in combination with libusb_wrap_sys_device(),
+        * which can access a device directly without prior device scanning.
+        *
+        * This is typically needed on Android, where access to USB devices
+        * is limited.
+        *
+        * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY
+        *
+        * Only valid on Linux.
         */
-       LIBUSB_OPTION_WEAK_AUTHORITY = 2
+       LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2,
+
+#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
+
+       LIBUSB_OPTION_MAX = 3
 };
 
 int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 #if defined(__cplusplus)
 }
 #endif
index 07c7d23dad6b77f61ec7a8ad9b76c72af575ee2c..192d06fe3d16e02f22a99b8a268996b8a0e97186 100644 (file)
@@ -18,8 +18,7 @@ SOURCES = core.c \
   os/events_posix.c \
   os/threads_posix.c
 
-HEADERS = hotplug.h \
-  libusb.h \
+HEADERS = libusb.h \
   libusbi.h \
   version.h \
   version_nano.h \
index 491114be94a391a558f4aeec34a9a28f8c7a9fbd..b1fc88c991338dc9c60726dcde9bb19aa8404525 100644 (file)
 #define PTR_ALIGN(v) \
        (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
 
+/* Atomic operations
+ *
+ * Useful for reference counting or when accessing a value without a lock
+ *
+ * The following atomic operations are defined:
+ *   usbi_atomic_load() - Atomically read a variable's value
+ *   usbi_atomic_store() - Atomically write a new value value to a variable
+ *   usbi_atomic_inc() - Atomically increment a variable's value and return the new value
+ *   usbi_atomic_dec() - Atomically decrement a variable's value and return the new value
+ *
+ * All of these operations are ordered with each other, thus the effects of
+ * any one operation is guaranteed to be seen by any other operation.
+ */
+#ifdef _MSC_VER
+typedef volatile LONG usbi_atomic_t;
+#define usbi_atomic_load(a)    (*(a))
+#define usbi_atomic_store(a, v)        (*(a)) = (v)
+#define usbi_atomic_inc(a)     InterlockedIncrement((a))
+#define usbi_atomic_dec(a)     InterlockedDecrement((a))
+#else
+#include <stdatomic.h>
+typedef atomic_long usbi_atomic_t;
+#define usbi_atomic_load(a)    atomic_load((a))
+#define usbi_atomic_store(a, v)        atomic_store((a), (v))
+#define usbi_atomic_inc(a)     (atomic_fetch_add((a), 1) + 1)
+#define usbi_atomic_dec(a)     (atomic_fetch_add((a), -1) - 1)
+#endif
+
 /* Internal abstractions for event handling and thread synchronization */
 #if defined(PLATFORM_POSIX)
 #include "os/events_posix.h"
@@ -289,22 +317,23 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
 #define usbi_err(ctx, ...)     _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
 #define usbi_warn(ctx, ...)    _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
 #define usbi_info(ctx, ...)    _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_dbg(...)          _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define usbi_dbg(ctx ,...)             _usbi_log(ctx, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
 
 #else /* ENABLE_LOGGING */
 
 #define usbi_err(ctx, ...)     UNUSED(ctx)
 #define usbi_warn(ctx, ...)    UNUSED(ctx)
 #define usbi_info(ctx, ...)    UNUSED(ctx)
-#define usbi_dbg(...)          do {} while (0)
+#define usbi_dbg(ctx, ...)     do {} while (0)
 
 #endif /* ENABLE_LOGGING */
 
 #define DEVICE_CTX(dev)                ((dev)->ctx)
-#define HANDLE_CTX(handle)     (DEVICE_CTX((handle)->dev))
-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
+#define HANDLE_CTX(handle)     ((handle) ? DEVICE_CTX((handle)->dev) : NULL)
 #define ITRANSFER_CTX(itransfer) \
-       (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)))
+       ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL)
+#define TRANSFER_CTX(transfer) \
+       (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)))
 
 #define IS_EPIN(ep)            (0 != ((ep) & LIBUSB_ENDPOINT_IN))
 #define IS_EPOUT(ep)           (!IS_EPIN(ep))
@@ -340,6 +369,9 @@ struct libusb_context {
        libusb_hotplug_callback_handle next_hotplug_cb_handle;
        usbi_mutex_t hotplug_cbs_lock;
 
+       /* A flag to indicate that the context is ready for hotplug notifications */
+       usbi_atomic_t hotplug_ready;
+
        /* this is a list of in-flight transfer handles, sorted by timeout
         * expiration. URBs to timeout the soonest are placed at the beginning of
         * the list, URBs that will time out later are placed after, and urbs with
@@ -404,13 +436,26 @@ struct libusb_context {
 };
 
 extern struct libusb_context *usbi_default_context;
+extern struct libusb_context *usbi_fallback_context;
 
 extern struct list_head active_contexts_list;
 extern usbi_mutex_static_t active_contexts_lock;
 
 static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
 {
-       return ctx ? ctx : usbi_default_context;
+       static int warned = 0;
+
+       if (!ctx) {
+               ctx = usbi_default_context;
+       }
+       if (!ctx) {
+               ctx = usbi_fallback_context;
+               if (ctx && warned == 0) {
+                       usbi_err(ctx, "API misuse! Using non-default context as implicit default.");
+                       warned = 1;
+               }
+       }
+       return ctx;
 }
 
 enum usbi_event_flags {
@@ -450,10 +495,7 @@ static inline void usbi_end_event_handling(struct libusb_context *ctx)
 }
 
 struct libusb_device {
-       /* lock protects refcnt, everything else is finalized at initialization
-        * time */
-       usbi_mutex_t lock;
-       int refcnt;
+       usbi_atomic_t refcnt;
 
        struct libusb_context *ctx;
        struct libusb_device *parent_dev;
@@ -467,7 +509,7 @@ struct libusb_device {
        unsigned long session_data;
 
        struct libusb_device_descriptor device_descriptor;
-       int attached;
+       usbi_atomic_t attached;
 };
 
 struct libusb_device_handle {
@@ -534,6 +576,10 @@ struct usbi_transfer {
        uint32_t state_flags;   /* Protected by usbi_transfer->lock */
        uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
 
+       /* The device reference is held until destruction for logging
+        * even after dev_handle is set to NULL.  */
+       struct libusb_device *dev;
+
        /* this lock is held during libusb_submit_transfer() and
         * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
         * cancellation, submission-during-cancellation, etc). the OS backend
@@ -664,8 +710,75 @@ union usbi_bos_desc_buf {
         uint16_t align;         /* Force 2-byte alignment */
 };
 
+enum usbi_hotplug_flags {
+       /* This callback is interested in device arrivals */
+       USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+
+       /* This callback is interested in device removals */
+       USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+
+       /* IMPORTANT: The values for the below entries must start *after*
+        * the highest value of the above entries!!!
+        */
+
+       /* The vendor_id field is valid for matching */
+       USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
+
+       /* The product_id field is valid for matching */
+       USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
+
+       /* The dev_class field is valid for matching */
+       USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
+
+       /* This callback has been unregistered and needs to be freed */
+       USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
+};
+
+struct usbi_hotplug_callback {
+       /* Flags that control how this callback behaves */
+       uint8_t flags;
+
+       /* Vendor ID to match (if flags says this is valid) */
+       uint16_t vendor_id;
+
+       /* Product ID to match (if flags says this is valid) */
+       uint16_t product_id;
+
+       /* Device class to match (if flags says this is valid) */
+       uint8_t dev_class;
+
+       /* Callback function to invoke for matching event/device */
+       libusb_hotplug_callback_fn cb;
+
+       /* Handle for this callback (used to match on deregister) */
+       libusb_hotplug_callback_handle handle;
+
+       /* User data that will be passed to the callback function */
+       void *user_data;
+
+       /* List this callback is registered in (ctx->hotplug_cbs) */
+       struct list_head list;
+};
+
+struct usbi_hotplug_message {
+       /* The hotplug event that occurred */
+       libusb_hotplug_event event;
+
+       /* The device for which this hotplug event occurred */
+       struct libusb_device *device;
+
+       /* List this message is contained in (ctx->hotplug_msgs) */
+       struct list_head list;
+};
+
 /* shared data and functions */
 
+void usbi_hotplug_init(struct libusb_context *ctx);
+void usbi_hotplug_exit(struct libusb_context *ctx);
+void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
+       libusb_hotplug_event event);
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs);
+
 int usbi_io_init(struct libusb_context *ctx);
 void usbi_io_exit(struct libusb_context *ctx);
 
@@ -696,6 +809,13 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle
        short poll_events);
 void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
 
+struct usbi_option {
+  int is_set;
+  union {
+    int ival;
+  } arg;
+};
+
 /* OS event abstraction */
 
 int usbi_create_event(usbi_event_t *event);
@@ -793,7 +913,8 @@ struct usbi_os_backend {
         * data structures for later, etc.
         *
         * This function is called when a libusb user initializes the library
-        * prior to use.
+        * prior to use. Mutual exclusion with other init and exit calls is
+        * guaranteed when this function is called.
         *
         * Return 0 on success, or a LIBUSB_ERROR code on failure.
         */
@@ -803,6 +924,8 @@ struct usbi_os_backend {
         * that was set up by init.
         *
         * This function is called when the user deinitializes the library.
+        * Mutual exclusion with other init and exit calls is guaranteed when
+        * this function is called.
         */
        void (*exit)(struct libusb_context *ctx);
 
@@ -1365,6 +1488,12 @@ extern const struct usbi_os_backend usbi_backend;
 #define for_each_removed_event_source_safe(ctx, e, n) \
        for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
 
+#define for_each_hotplug_cb(ctx, c) \
+       for_each_helper(c, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
+#define for_each_hotplug_cb_safe(ctx, c, n) \
+       for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
 #ifdef __cplusplus
 }
 #endif
index e415589d670f071e6acb03247ba7b448fdb1e096..388dbca6077b9aed782e5f0d92e34d2a35596a50 100644 (file)
@@ -1,8 +1,8 @@
 /* -*- Mode: C; indent-tabs-mode:nil -*- */
 /*
  * darwin backend for libusb 1.0
- * Copyright Â© 2008-2020 Nathan Hjelm <hjelmn@cs.unm.edu>
- * Copyright Â© 2019-2020 Google LLC. All rights reserved.
+ * Copyright Â© 2008-2021 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright Â© 2019-2021 Google LLC. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * function. Its use is also conditionalized to only older deployment targets. */
 #define OBJC_SILENCE_GC_DEPRECATIONS 1
 
+/* Default timeout to 10s for reenumerate. This is needed because USBDeviceReEnumerate
+ * does not return error status on macOS. */
+#define DARWIN_REENUMERATE_TIMEOUT_US (10 * USEC_PER_SEC)
+
 #include <AvailabilityMacros.h>
 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
   #include <objc/objc-auto.h>
 
 #include "darwin_usb.h"
 
-static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int init_count = 0;
 
+/* Both kIOMasterPortDefault or kIOMainPortDefault are synonyms for 0. */
+static const mach_port_t darwin_default_master_port = 0;
+
 /* async event thread */
 static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t  libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
@@ -77,14 +83,17 @@ static pthread_t libusb_darwin_at;
 static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
 static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
 static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
+static int darwin_reenumerate_device(struct libusb_device_handle *dev_handle, bool capture);
+static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
 static int darwin_reset_device(struct libusb_device_handle *dev_handle);
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface);
 static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
 
 static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
 static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
                                              UInt64 old_session_id);
 
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
                                                   UInt64 *old_session_id);
 
 #if defined(ENABLE_LOGGING)
@@ -102,6 +111,9 @@ static const char *darwin_error_str (IOReturn result) {
   case kIOReturnExclusiveAccess:
     return "another process has device opened for exclusive access";
   case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+  case kUSBHostReturnPipeStalled:
+#endif
     return "pipe is stalled";
   case kIOReturnError:
     return "could not establish a connection to the Darwin kernel";
@@ -141,16 +153,20 @@ static enum libusb_error darwin_to_libusb (IOReturn result) {
   case kIOReturnExclusiveAccess:
     return LIBUSB_ERROR_ACCESS;
   case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+  case kUSBHostReturnPipeStalled:
+#endif
     return LIBUSB_ERROR_PIPE;
   case kIOReturnBadArgument:
     return LIBUSB_ERROR_INVALID_PARAM;
   case kIOUSBTransactionTimeout:
     return LIBUSB_ERROR_TIMEOUT;
+  case kIOUSBUnknownPipeErr:
+    return LIBUSB_ERROR_NOT_FOUND;
   case kIOReturnNotResponding:
   case kIOReturnAborted:
   case kIOReturnError:
   case kIOUSBNoAsyncPortErr:
-  case kIOUSBUnknownPipeErr:
   default:
     return LIBUSB_ERROR_OTHER;
   }
@@ -167,6 +183,7 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev)
       (*(cached_dev->device))->Release(cached_dev->device);
       cached_dev->device = NULL;
     }
+    IOObjectRelease (cached_dev->service);
     free (cached_dev);
   }
 }
@@ -183,7 +200,9 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
 
   uint8_t i, iface;
 
-  usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
+  struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+
+  usbi_dbg (ctx, "converting ep address 0x%02x to pipeRef and interface", ep);
 
   for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
     cInterface = &priv->interfaces[iface];
@@ -199,7 +218,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
           if (interface_out)
             *interface_out = cInterface;
 
-          usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+          usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface);
           return LIBUSB_SUCCESS;
         }
       }
@@ -240,7 +259,7 @@ static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32
       CFRelease (locationCF);
   }
 
-  return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
+  return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator);
 }
 
 /* Returns 1 on success, 0 on failure. */
@@ -281,7 +300,7 @@ static bool get_ioregistry_value_data (io_service_t service, CFStringRef propert
   return success;
 }
 
-static usb_device_t **darwin_device_from_service (io_service_t service)
+static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service)
 {
   io_cf_plugin_ref_t *plugInInterface = NULL;
   usb_device_t **device;
@@ -300,14 +319,14 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
       break;
     }
 
-    usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
+    usbi_dbg (ctx, "set up plugin for service retry: %s", darwin_error_str (kresult));
 
     /* sleep for a little while before trying again */
     nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
   }
 
   if (kIOReturnSuccess != kresult || !plugInInterface) {
-    usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
+    usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult));
     return NULL;
   }
 
@@ -330,7 +349,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
   usbi_mutex_lock(&active_contexts_lock);
 
   while ((service = IOIteratorNext(add_devices))) {
-    ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+    ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id);
     if (ret < 0 || !cached_device->can_enumerate) {
       continue;
     }
@@ -341,7 +360,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
     }
 
     if (cached_device->in_reenumerate) {
-      usbi_dbg ("cached device in reset state. reset complete...");
+      usbi_dbg (NULL, "cached device in reset state. reset complete...");
       cached_device->in_reenumerate = false;
     }
 
@@ -358,7 +377,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
   struct darwin_cached_device *old_device;
 
   io_service_t device;
-  UInt64 session;
+  UInt64 session, locationID;
   int ret;
 
   usbi_mutex_lock(&active_contexts_lock);
@@ -368,6 +387,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
 
     /* get the location from the i/o registry */
     ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+    (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
     IOObjectRelease (device);
     if (!ret)
       continue;
@@ -380,7 +400,8 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
         if (old_device->in_reenumerate) {
           /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
            * will deref if needed. */
-          usbi_dbg ("detected device detached due to re-enumeration");
+          usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64,
+                    session, locationID);
 
           /* the device object is no longer usable so go ahead and release it */
           if (old_device->device) {
@@ -403,7 +424,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
     }
 
     for_each_context(ctx) {
-      usbi_dbg ("notifying context %p of device disconnect", ctx);
+      usbi_dbg (ctx, "notifying context %p of device disconnect", ctx);
 
       dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
       if (dev) {
@@ -426,7 +447,7 @@ static void darwin_hotplug_poll (void)
   /* since a kernel thread may notify the IOIterators used for
    * hotplug notification we can't just clear the iterators.
    * instead just wait until all IOService providers are quiet */
-  (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
+  (void) IOKitWaitQuiet (darwin_default_master_port, &timeout);
 }
 
 static void darwin_clear_iterator (io_iterator_t iter) {
@@ -473,7 +494,8 @@ static void *darwin_event_thread_main (void *arg0) {
   io_iterator_t          libusb_rem_device_iterator;
   io_iterator_t          libusb_add_device_iterator;
 
-  usbi_dbg ("creating hotplug event source");
+  /* ctx must only be used for logging during thread startup */
+  usbi_dbg (ctx, "creating hotplug event source");
 
   runloop = CFRunLoopGetCurrent ();
   CFRetain (runloop);
@@ -486,7 +508,7 @@ static void *darwin_event_thread_main (void *arg0) {
   CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
 
   /* add the notification port to the run loop */
-  libusb_notification_port     = IONotificationPortCreate (kIOMasterPortDefault);
+  libusb_notification_port     = IONotificationPortCreate (darwin_default_master_port);
   libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
   CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
 
@@ -494,7 +516,7 @@ static void *darwin_event_thread_main (void *arg0) {
   kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
                                               IOServiceMatching(darwin_device_class),
                                               darwin_devices_detached,
-                                              ctx, &libusb_rem_device_iterator);
+                                              NULL, &libusb_rem_device_iterator);
 
   if (kresult != kIOReturnSuccess) {
     usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
@@ -507,7 +529,7 @@ static void *darwin_event_thread_main (void *arg0) {
   kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
                                               IOServiceMatching(darwin_device_class),
                                               darwin_devices_attached,
-                                              ctx, &libusb_add_device_iterator);
+                                              NULL, &libusb_add_device_iterator);
 
   if (kresult != kIOReturnSuccess) {
     usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
@@ -520,7 +542,7 @@ static void *darwin_event_thread_main (void *arg0) {
   darwin_clear_iterator (libusb_rem_device_iterator);
   darwin_clear_iterator (libusb_add_device_iterator);
 
-  usbi_dbg ("darwin event thread ready to receive events");
+  usbi_dbg (ctx, "darwin event thread ready to receive events");
 
   /* signal the main thread that the hotplug runloop has been created. */
   pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -532,7 +554,7 @@ static void *darwin_event_thread_main (void *arg0) {
   /* run the runloop */
   CFRunLoopRun();
 
-  usbi_dbg ("darwin event thread exiting");
+  usbi_dbg (NULL, "darwin event thread exiting");
 
   /* signal the main thread that the hotplug runloop has finished. */
   pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -567,23 +589,20 @@ static void darwin_cleanup_devices(void) {
   list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
     darwin_deref_cached_device(dev);
   }
-
-  darwin_cached_devices.prev = darwin_cached_devices.next = NULL;
 }
 
 static int darwin_init(struct libusb_context *ctx) {
   bool first_init;
   int rc;
 
-  pthread_mutex_lock (&libusb_darwin_init_mutex);
-
   first_init = (1 == ++init_count);
 
   do {
     if (first_init) {
-      assert (NULL == darwin_cached_devices.next);
-      list_init (&darwin_cached_devices);
-
+      if (NULL == darwin_cached_devices.next) {
+        list_init (&darwin_cached_devices);
+      }
+      assert(list_empty(&darwin_cached_devices));
 #if !defined(HAVE_CLOCK_GETTIME)
       /* create the clocks that will be used if clock_gettime() is not available */
       host_name_port_t host_self;
@@ -632,16 +651,12 @@ static int darwin_init(struct libusb_context *ctx) {
     --init_count;
   }
 
-  pthread_mutex_unlock (&libusb_darwin_init_mutex);
-
   return rc;
 }
 
 static void darwin_exit (struct libusb_context *ctx) {
   UNUSED(ctx);
 
-  pthread_mutex_lock (&libusb_darwin_init_mutex);
-
   if (0 == --init_count) {
     /* stop the event runloop and wait for the thread to terminate. */
     pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -659,8 +674,6 @@ static void darwin_exit (struct libusb_context *ctx) {
     mach_port_deallocate(mach_task_self(), clock_monotonic);
 #endif
   }
-
-  pthread_mutex_unlock (&libusb_darwin_init_mutex);
 }
 
 static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
@@ -744,7 +757,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx,
      not usable anyway */
   if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) &&
       0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) {
-    usbi_dbg ("ignoring configuration on root hub simulation");
+    usbi_dbg (ctx, "ignoring configuration on root hub simulation");
     dev->active_config = 0;
     return LIBUSB_SUCCESS;
   }
@@ -787,7 +800,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx,
     /* not configured */
     dev->active_config = 0;
 
-  usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
+  usbi_dbg (ctx, "active config: %u, first config: %u", dev->active_config, dev->first_config);
 
   return LIBUSB_SUCCESS;
 }
@@ -812,7 +825,7 @@ static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UI
   return (*device)->DeviceRequestTO (device, &req);
 }
 
-static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) {
+static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
   usb_device_t **device = dev->device;
   int retries = 1;
   long delay = 30000; // microseconds
@@ -850,7 +863,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
                                     0 == dev->dev_descriptor.bcdUSB)) {
       /* work around for incorrectly configured devices */
       if (try_reconfigure && is_open) {
-        usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
+        usbi_dbg(ctx, "descriptor appears to be invalid. resetting configuration before trying again...");
 
         /* set the first configuration */
         (*device)->SetConfiguration(device, 1);
@@ -881,7 +894,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
         if (kIOReturnSuccess != ret2) {
           /* prevent log spew from poorly behaving devices.  this indicates the
              os actually had trouble communicating with the device */
-          usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
+          usbi_dbg(ctx, "could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
         } else
           unsuspended = 1;
 
@@ -890,7 +903,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
     }
 
     if (kIOReturnSuccess != ret) {
-      usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
+      usbi_dbg(ctx, "kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
       /* sleep for a little while before trying again */
       nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL);
     }
@@ -906,10 +919,10 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
   if (ret != kIOReturnSuccess) {
     /* a debug message was already printed out for this error */
     if (LIBUSB_CLASS_HUB == bDeviceClass)
-      usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+      usbi_dbg (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
                 idVendor, idProduct, darwin_error_str (ret), ret);
     else
-      usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+      usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
                  idVendor, idProduct, darwin_error_str (ret), ret);
     return darwin_to_libusb (ret);
   }
@@ -922,20 +935,20 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
     return LIBUSB_ERROR_NO_DEVICE;
   }
 
-  usbi_dbg ("cached device descriptor:");
-  usbi_dbg ("  bDescriptorType:    0x%02x", dev->dev_descriptor.bDescriptorType);
-  usbi_dbg ("  bcdUSB:             0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
-  usbi_dbg ("  bDeviceClass:       0x%02x", dev->dev_descriptor.bDeviceClass);
-  usbi_dbg ("  bDeviceSubClass:    0x%02x", dev->dev_descriptor.bDeviceSubClass);
-  usbi_dbg ("  bDeviceProtocol:    0x%02x", dev->dev_descriptor.bDeviceProtocol);
-  usbi_dbg ("  bMaxPacketSize0:    0x%02x", dev->dev_descriptor.bMaxPacketSize0);
-  usbi_dbg ("  idVendor:           0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
-  usbi_dbg ("  idProduct:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
-  usbi_dbg ("  bcdDevice:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
-  usbi_dbg ("  iManufacturer:      0x%02x", dev->dev_descriptor.iManufacturer);
-  usbi_dbg ("  iProduct:           0x%02x", dev->dev_descriptor.iProduct);
-  usbi_dbg ("  iSerialNumber:      0x%02x", dev->dev_descriptor.iSerialNumber);
-  usbi_dbg ("  bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
+  usbi_dbg (ctx, "cached device descriptor:");
+  usbi_dbg (ctx, "  bDescriptorType:    0x%02x", dev->dev_descriptor.bDescriptorType);
+  usbi_dbg (ctx, "  bcdUSB:             0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
+  usbi_dbg (ctx, "  bDeviceClass:       0x%02x", dev->dev_descriptor.bDeviceClass);
+  usbi_dbg (ctx, "  bDeviceSubClass:    0x%02x", dev->dev_descriptor.bDeviceSubClass);
+  usbi_dbg (ctx, "  bDeviceProtocol:    0x%02x", dev->dev_descriptor.bDeviceProtocol);
+  usbi_dbg (ctx, "  bMaxPacketSize0:    0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+  usbi_dbg (ctx, "  idVendor:           0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
+  usbi_dbg (ctx, "  idProduct:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+  usbi_dbg (ctx, "  bcdDevice:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
+  usbi_dbg (ctx, "  iManufacturer:      0x%02x", dev->dev_descriptor.iManufacturer);
+  usbi_dbg (ctx, "  iProduct:           0x%02x", dev->dev_descriptor.iProduct);
+  usbi_dbg (ctx, "  iSerialNumber:      0x%02x", dev->dev_descriptor.iSerialNumber);
+  usbi_dbg (ctx, "  bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
 
   dev->can_enumerate = 1;
 
@@ -979,7 +992,7 @@ static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_ses
   return false;
 }
 
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
                                                   UInt64 *old_session_id) {
   struct darwin_cached_device *new_device;
   UInt64 sessionID = 0, parent_sessionID = 0;
@@ -996,28 +1009,28 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
   (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
   (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
   if (!get_device_port (service, &port)) {
-    usbi_dbg("could not get connected port number");
+    usbi_dbg(ctx, "could not get connected port number");
   }
 
-  usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
+  usbi_dbg(ctx, "finding cached device for sessionID 0x%" PRIx64, sessionID);
 
   if (get_device_parent_sessionID(service, &parent_sessionID)) {
-    usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
+    usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID);
   }
 
   usbi_mutex_lock(&darwin_cached_devices_lock);
   do {
     list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
-      usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+      usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
                sessionID, locationID, new_device->session, new_device->location);
       if (new_device->location == locationID && new_device->in_reenumerate) {
-        usbi_dbg ("found cached device with matching location that is being re-enumerated");
+        usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated");
         *old_session_id = new_device->session;
         break;
       }
 
       if (new_device->session == sessionID) {
-        usbi_dbg("using cached device for device");
+        usbi_dbg(ctx, "using cached device for device");
         *cached_out = new_device;
         break;
       }
@@ -1026,9 +1039,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
     if (*cached_out)
       break;
 
-    usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+    usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID);
 
-    device = darwin_device_from_service (service);
+    device = darwin_device_from_service (ctx, service);
     if (!device) {
       ret = LIBUSB_ERROR_NO_DEVICE;
       break;
@@ -1052,6 +1065,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
       (*device)->GetLocationID (device, &new_device->location);
       new_device->port = port;
       new_device->parent_session = parent_sessionID;
+    } else {
+      /* release the ref to old device's service */
+      IOObjectRelease (new_device->service);
     }
 
     /* keep track of devices regardless of if we successfully enumerate them to
@@ -1060,9 +1076,13 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
 
     new_device->session = sessionID;
     new_device->device = device;
+    new_device->service = service;
+
+    /* retain the service */
+    IOObjectRetain (service);
 
     /* cache the device descriptor */
-    ret = darwin_cache_device_descriptor(new_device);
+    ret = darwin_cache_device_descriptor(ctx, new_device);
     if (ret)
       break;
 
@@ -1094,14 +1114,14 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
       break;
 
     if (0 != old_session_id) {
-      usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
+      usbi_dbg (ctx, "re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
                 ctx, old_session_id, cached_device->session);
       /* save the libusb device before the session id is updated */
       dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id);
     }
 
     if (!dev) {
-      usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+      usbi_dbg (ctx, "allocating new device in context %p for with session 0x%" PRIx64,
                 ctx, cached_device->session);
 
       dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
@@ -1114,6 +1134,8 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
       priv->dev = cached_device;
       darwin_ref_cached_device (priv->dev);
       dev->port_number    = cached_device->port;
+      /* the location ID encodes the path to the device. the top byte of the location ID contains the bus number
+         (numbered from 0). the remaining bytes can be used to construct the device tree for that bus. */
       dev->bus_number     = cached_device->location >> 24;
       assert(cached_device->address <= UINT8_MAX);
       dev->device_address = (uint8_t)cached_device->address;
@@ -1127,10 +1149,13 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
     usbi_localize_device_descriptor(&dev->device_descriptor);
     dev->session_data = cached_device->session;
 
+    if (NULL != dev->parent_dev) {
+      libusb_unref_device(dev->parent_dev);
+      dev->parent_dev = NULL;
+    }
+
     if (cached_device->parent_session > 0) {
       dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
-    } else {
-      dev->parent_dev = NULL;
     }
 
     (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
@@ -1139,7 +1164,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
     case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
     case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
     case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
     case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
 #endif
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
@@ -1153,7 +1178,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
     if (ret < 0)
       break;
 
-    usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+    usbi_dbg (ctx, "found device with address %d port = %d parent = %p at %p", dev->device_address,
               dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
 
   } while (0);
@@ -1180,7 +1205,7 @@ static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) {
     return darwin_to_libusb (kresult);
 
   while ((service = IOIteratorNext (deviceIterator))) {
-    ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+    ret = darwin_get_cached_device (ctx, service, &cached_device, &old_session_id);
     if (ret < 0 || !cached_device->can_enumerate) {
       continue;
     }
@@ -1232,14 +1257,14 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
 
     CFRetain (libusb_darwin_acfl);
 
-    /* add the cfSource to the aync run loop */
+    /* add the cfSource to the async run loop */
     CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
   }
 
   /* device opened successfully */
   dpriv->open_count++;
 
-  usbi_dbg ("device open for access");
+  usbi_dbg (HANDLE_CTX(dev_handle), "device open for access");
 
   return 0;
 }
@@ -1257,6 +1282,10 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
   }
 
   dpriv->open_count--;
+  if (NULL == dpriv->device) {
+    usbi_warn (HANDLE_CTX (dev_handle), "darwin_close device missing IOService");
+    return;
+  }
 
   /* make sure all interfaces are released */
   for (i = 0 ; i < USB_MAXINTERFACES ; i++)
@@ -1362,28 +1391,39 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
 
   /* current interface */
   struct darwin_interface *cInterface = &priv->interfaces[iface];
+#if InterfaceVersion >= 550
+  IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+  UInt8 dont_care1, dont_care3;
+  UInt16 dont_care2;
+#endif
 
   IOReturn kresult;
 
   UInt8 numep, direction, number;
-  UInt8 dont_care1, dont_care3;
-  UInt16 dont_care2;
   int rc;
+  struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
 
-  usbi_dbg ("building table of endpoints.");
+  usbi_dbg (ctx, "building table of endpoints.");
 
   /* retrieve the total number of endpoints on this interface */
   kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
   if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+    usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   /* iterate through pipe references */
   for (UInt8 i = 1 ; i <= numep ; i++) {
+#if InterfaceVersion >= 550
+    kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties);
+    number = pipeProperties.bEndpointNumber;
+    direction = pipeProperties.bDirection;
+#else
     kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
                                                             &dont_care2, &dont_care3);
-
+#endif
     if (kresult != kIOReturnSuccess) {
       /* probably a buggy device. try to get the endpoint address from the descriptors */
       struct libusb_config_descriptor *config;
@@ -1401,6 +1441,10 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
         return rc;
       }
 
+      if (iface >= config->bNumInterfaces) {
+        usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface);
+        return LIBUSB_ERROR_NOT_FOUND;
+      }
       endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1;
 
       cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
@@ -1408,7 +1452,7 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
       cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
     }
 
-    usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
+    usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
               cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK);
   }
 
@@ -1429,30 +1473,32 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   /* current interface */
   struct darwin_interface *cInterface = &priv->interfaces[iface];
 
+  struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
   kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
   /* make sure we have an interface */
   if (!usbInterface && dpriv->first_config != 0) {
-    usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+    usbi_info (ctx, "no interface found; setting configuration: %d", dpriv->first_config);
 
     /* set the configuration */
     ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config);
     if (ret != LIBUSB_SUCCESS) {
-      usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+      usbi_err (ctx, "could not set configuration");
       return ret;
     }
 
     kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
     if (kresult != kIOReturnSuccess) {
-      usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+      usbi_err (ctx, "darwin_get_interface: %s", darwin_error_str(kresult));
       return darwin_to_libusb (kresult);
     }
   }
 
   if (!usbInterface) {
-    usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+    usbi_info (ctx, "interface not found");
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
@@ -1464,12 +1510,12 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   (void)IOObjectRelease (usbInterface);
 
   if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
+    usbi_err (ctx, "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   if (!plugInInterface) {
-    usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+    usbi_err (ctx, "plugin interface not found");
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
@@ -1481,14 +1527,14 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
   (*plugInInterface)->Release (plugInInterface);
   if (kresult != kIOReturnSuccess || !cInterface->interface) {
-    usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
+    usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   /* claim the interface */
   kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
   if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
+    usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
@@ -1497,7 +1543,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   if (ret) {
     /* this should not happen */
     darwin_release_interface (dev_handle, iface);
-    usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+    usbi_err (ctx, "could not build endpoint table");
     return ret;
   }
 
@@ -1506,7 +1552,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   /* create async event source */
   kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
   if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+    usbi_err (ctx, "could not create async event source");
 
     /* can't continue without an async event source */
     (void)darwin_release_interface (dev_handle, iface);
@@ -1517,7 +1563,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
   /* add the cfSource to the async thread's run loop */
   CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
 
-  usbi_dbg ("interface opened");
+  usbi_dbg (ctx, "interface opened");
 
   return LIBUSB_SUCCESS;
 }
@@ -1540,6 +1586,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin
   if (cInterface->cfSource) {
     CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
     CFRelease (cInterface->cfSource);
+    cInterface->cfSource = NULL;
   }
 
   kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
@@ -1555,6 +1602,30 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin
   return darwin_to_libusb (kresult);
 }
 
+static int check_alt_setting_and_clear_halt(struct libusb_device_handle *dev_handle, uint8_t altsetting, struct darwin_interface *cInterface) {
+  enum libusb_error ret;
+  IOReturn kresult;
+  uint8_t current_alt_setting;
+
+  kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &current_alt_setting);
+  if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) {
+    return LIBUSB_ERROR_PIPE;
+  }
+
+  for (int i = 0 ; i < cInterface->num_endpoints ; i++) {
+    ret = darwin_clear_halt(dev_handle, cInterface->endpoint_addrs[i]);
+    if (LIBUSB_SUCCESS != ret) {
+      usbi_warn(HANDLE_CTX (dev_handle), "error clearing pipe halt for endpoint %d", i);
+      if (LIBUSB_ERROR_NOT_FOUND == ret) {
+        /* may need to re-open the interface */
+        return ret;
+      }
+    }
+  }
+
+  return LIBUSB_SUCCESS;
+}
+
 static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) {
   struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   IOReturn kresult;
@@ -1567,19 +1638,41 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
     return LIBUSB_ERROR_NO_DEVICE;
 
   kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
-  if (kresult != kIOReturnSuccess)
-    darwin_reset_device (dev_handle);
+  if (kresult == kIOReturnSuccess) {
+    /* update the list of endpoints */
+    ret = get_endpoints (dev_handle, iface);
+    if (ret) {
+      /* this should not happen */
+      darwin_release_interface (dev_handle, iface);
+      usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+    }
+    return ret;
+  }
 
-  /* update list of endpoints */
-  ret = get_endpoints (dev_handle, iface);
-  if (ret) {
-    /* this should not happen */
-    darwin_release_interface (dev_handle, iface);
-    usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+  usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult));
+
+  ret = darwin_to_libusb(kresult);
+  if (ret != LIBUSB_ERROR_PIPE) {
     return ret;
   }
 
-  return darwin_to_libusb (kresult);
+  /* If a device only supports a default setting for the specified interface, then a STALL
+     (kIOUSBPipeStalled) may be returned. Ref: USB 2.0 specs 9.4.10.
+     Mimic the behaviour in e.g. the Linux kernel: in such case, reset all endpoints
+     of the interface (as would have been done per 9.1.1.5) and return success. */
+
+  ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+  if (LIBUSB_ERROR_NOT_FOUND == ret) {
+    /* For some reason we need to reclaim the interface after the pipe error with some versions of macOS */
+    ret = darwin_claim_interface (dev_handle, iface);
+    if (LIBUSB_SUCCESS != ret) {
+      darwin_release_interface (dev_handle, iface);
+      usbi_err (HANDLE_CTX (dev_handle), "could not reclaim interface: %s", darwin_error_str(kresult));
+    }
+    ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+  }
+
+  return ret;
 }
 
 static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
@@ -1610,6 +1703,8 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
   int open_count = dpriv->open_count;
   int ret;
 
+  struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
   /* clear claimed interfaces temporarily */
   dev_handle->claimed_interfaces = 0;
 
@@ -1629,16 +1724,16 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
   }
 
   if (dpriv->active_config != active_config) {
-    usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config);
+    usbi_dbg (ctx, "darwin/restore_state: restoring configuration %d...", active_config);
 
     ret = darwin_set_configuration (dev_handle, active_config);
     if (LIBUSB_SUCCESS != ret) {
-      usbi_dbg ("darwin/restore_state: could not restore configuration");
+      usbi_dbg (ctx, "darwin/restore_state: could not restore configuration");
       return LIBUSB_ERROR_NOT_FOUND;
     }
   }
 
-  usbi_dbg ("darwin/restore_state: reclaiming interfaces");
+  usbi_dbg (ctx, "darwin/restore_state: reclaiming interfaces");
 
   if (claimed_interfaces) {
     for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) {
@@ -1646,11 +1741,11 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
         continue;
       }
 
-      usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface);
+      usbi_dbg (ctx, "darwin/restore_state: re-claiming interface %u", iface);
 
       ret = darwin_claim_interface (dev_handle, iface);
       if (LIBUSB_SUCCESS != ret) {
-        usbi_dbg ("darwin/restore_state: could not claim interface %u", iface);
+        usbi_dbg (ctx, "darwin/restore_state: could not claim interface %u", iface);
         return LIBUSB_ERROR_NOT_FOUND;
       }
 
@@ -1658,21 +1753,24 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
     }
   }
 
-  usbi_dbg ("darwin/restore_state: device state restored");
+  usbi_dbg (ctx, "darwin/restore_state: device state restored");
 
   return LIBUSB_SUCCESS;
 }
 
-static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
+static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
   unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
   int8_t active_config = dpriv->active_config;
+  UInt32 options = 0;
   IOUSBDeviceDescriptor descriptor;
   IOUSBConfigurationDescriptorPtr cached_configuration;
   IOUSBConfigurationDescriptor *cached_configurations;
   IOReturn kresult;
   UInt8 i;
 
+  struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
   if (dpriv->in_reenumerate) {
     /* ack, two (or more) threads are trying to reset the device! abort! */
     return LIBUSB_ERROR_NOT_FOUND;
@@ -1689,62 +1787,152 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
     memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
   }
 
+  /* if we need to release capture */
+  if (HAS_CAPTURE_DEVICE()) {
+    if (capture) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
+      options |= kUSBReEnumerateCaptureDeviceMask;
+#endif
+    }
+  } else {
+    capture = false;
+  }
+
   /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
-  kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+  kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options);
   if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
+    usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
     dpriv->in_reenumerate = false;
     return darwin_to_libusb (kresult);
   }
 
-  usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete...");
+  /* capture mode does not re-enumerate but it does require re-open */
+  if (capture) {
+    usbi_dbg (ctx, "darwin/reenumerate_device: restoring state...");
+    dpriv->in_reenumerate = false;
+    return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
+  }
+
+  usbi_dbg (ctx, "darwin/reenumerate_device: waiting for re-enumeration to complete...");
+
+  struct timespec start;
+  usbi_get_monotonic_time(&start);
 
   while (dpriv->in_reenumerate) {
     struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
     nanosleep (&delay, NULL);
+
+    struct timespec now;
+    usbi_get_monotonic_time(&now);
+    unsigned long elapsed_us = (now.tv_sec - start.tv_sec) * USEC_PER_SEC +
+                                (now.tv_nsec - start.tv_nsec) / 1000;
+
+    if (elapsed_us >= DARWIN_REENUMERATE_TIMEOUT_US) {
+      usbi_err (ctx, "darwin/reenumerate_device: timeout waiting for reenumerate");
+      dpriv->in_reenumerate = false;
+      return LIBUSB_ERROR_TIMEOUT;
+    }
   }
 
   /* compare descriptors */
-  usbi_dbg ("darwin/reset_device: checking whether descriptors changed");
+  usbi_dbg (ctx, "darwin/reenumerate_device: checking whether descriptors changed");
 
   if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) {
     /* device descriptor changed. need to return not found. */
-    usbi_dbg ("darwin/reset_device: device descriptor changed");
+    usbi_dbg (ctx, "darwin/reenumerate_device: device descriptor changed");
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
   for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
     (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
     if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
-      usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i);
+      usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i);
       return LIBUSB_ERROR_NOT_FOUND;
     }
   }
 
-  usbi_dbg ("darwin/reset_device: device reset complete. restoring state...");
+  usbi_dbg (ctx, "darwin/reenumerate_device: device reset complete. restoring state...");
 
   return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
 }
 
-static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
-  io_service_t usbInterface;
-  CFTypeRef driver;
   IOReturn kresult;
+  enum libusb_error ret;
 
-  kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
-  if (kresult != kIOReturnSuccess) {
-    usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
-
-    return darwin_to_libusb (kresult);
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+  if (dpriv->capture_count > 0) {
+    /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */
+    kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+    ret = darwin_to_libusb (kresult);
+  } else {
+    ret = darwin_reenumerate_device (dev_handle, false);
+  }
+#else
+  /* ResetDevice() is missing on non-macOS platforms */
+  ret = darwin_reenumerate_device (dev_handle, false);
+  if ((ret == LIBUSB_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND) && dpriv->capture_count > 0) {
+    int capture_count;
+    int8_t active_config = dpriv->active_config;
+    unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
+
+    /* save old capture_count */
+    capture_count = dpriv->capture_count;
+    /* reset capture count */
+    dpriv->capture_count = 0;
+    /* attempt to detach kernel driver again as it is now re-attached */
+    ret = darwin_detach_kernel_driver (dev_handle, 0);
+    if (ret != LIBUSB_SUCCESS) {
+      return ret;
+    }
+    /* restore capture_count */
+    dpriv->capture_count = capture_count;
+    /* restore configuration */
+    ret = darwin_restore_state (dev_handle, active_config, claimed_interfaces);
   }
+#endif
+  return ret;
+}
+
+static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) {
+  CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name);
+  CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
+                                                                        &kCFTypeDictionaryKeyCallBacks,
+                                                                        &kCFTypeDictionaryValueCallBacks);
+  CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+  CFTypeRef interfaceCF =  CFNumberCreate (NULL, kCFNumberSInt8Type, &interface_number);
 
-  driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
-  IOObjectRelease (usbInterface);
+  CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
+  CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
+  CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBHostMatchingPropertyInterfaceNumber), interfaceCF);
 
-  if (driver) {
-    CFRelease (driver);
+  CFRelease (interfaceCF);
+  CFRelease (locationCF);
+  CFRelease (propertyMatchDict);
+
+  return IOServiceGetMatchingService (darwin_default_master_port, matchingDict);
+}
+
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+  io_service_t usb_interface, child = IO_OBJECT_NULL;
+
+  /* locate the IO registry entry for this interface */
+  usb_interface = usb_find_interface_matching_location (kIOUSBHostInterfaceClassName, interface, dpriv->location);
+  if (0 == usb_interface) {
+    /* check for the legacy class entry */
+    usb_interface = usb_find_interface_matching_location (kIOUSBInterfaceClassName, interface, dpriv->location);
+    if (0 == usb_interface) {
+      return LIBUSB_ERROR_NOT_FOUND;
+    }
+  }
 
+  /* if the IO object has a child entry in the IO Registry it has a kernel driver attached */
+  (void) IORegistryEntryGetChildEntry (usb_interface, kIOServicePlane, &child);
+  IOObjectRelease (usb_interface);
+  if (IO_OBJECT_NULL != child) {
+    IOObjectRelease (child);
     return 1;
   }
 
@@ -1873,11 +2061,17 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
 
   IOReturn kresult;
-  uint8_t direction, number, interval, pipeRef, transferType;
-  uint16_t maxPacketSize;
+  uint8_t pipeRef, interval;
   UInt64 frame;
   AbsoluteTime atTime;
   int i;
+#if InterfaceVersion >= 550
+  IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+  /* None of the values below are used in libusb for iso transfers */
+  uint8_t direction, number, transferType;
+  uint16_t maxPacketSize;
+#endif
 
   struct darwin_interface *cInterface;
 
@@ -1909,8 +2103,20 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   }
 
   /* determine the properties of this endpoint and the speed of the device */
-  (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+#if InterfaceVersion >= 550
+  kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
+  interval = pipeProperties.bInterval;
+#else
+  kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
                                                  &transferType, &maxPacketSize, &interval);
+#endif
+  if (kresult != kIOReturnSuccess) {
+    usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult);
+    free(tpriv->isoc_framelist);
+    tpriv->isoc_framelist = NULL;
+
+    return darwin_to_libusb (kresult);
+  }
 
   /* Last but not least we need the bus frame number */
   kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
@@ -1922,9 +2128,6 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
     return darwin_to_libusb (kresult);
   }
 
-  (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
-                                                 &transferType, &maxPacketSize, &interval);
-
   /* schedule for a frame a little in the future */
   frame += 4;
 
@@ -2051,8 +2254,10 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
   uint8_t pipeRef, iface;
   IOReturn kresult;
 
+  struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
-    usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+    usbi_err (ctx, "endpoint not found on any open interface");
 
     return LIBUSB_ERROR_NOT_FOUND;
   }
@@ -2060,7 +2265,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
   if (!dpriv->device)
     return LIBUSB_ERROR_NO_DEVICE;
 
-  usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
+  usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef);
 
   /* abort transactions */
 #if InterfaceVersion >= 550
@@ -2070,7 +2275,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
 #endif
     (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
 
-  usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
+  usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit");
 
   /* newer versions of darwin support clearing additional bits on the device's endpoint */
   kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
@@ -2099,7 +2304,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0)
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
 
-  usbi_dbg ("an async io operation has completed");
+  usbi_dbg (TRANSFER_CTX(transfer), "an async io operation has completed");
 
   /* if requested write a zero packet */
   if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
@@ -2122,6 +2327,8 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer
   if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
     result = kIOUSBTransactionTimeout;
 
+  struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
   switch (result) {
   case kIOReturnUnderrun:
   case kIOReturnSuccess:
@@ -2129,17 +2336,17 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer
   case kIOReturnAborted:
     return LIBUSB_TRANSFER_CANCELLED;
   case kIOUSBPipeStalled:
-    usbi_dbg ("transfer error: pipe is stalled");
+    usbi_dbg (ctx, "transfer error: pipe is stalled");
     return LIBUSB_TRANSFER_STALL;
   case kIOReturnOverrun:
-    usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+    usbi_warn (ctx, "transfer error: data overrun");
     return LIBUSB_TRANSFER_OVERFLOW;
   case kIOUSBTransactionTimeout:
-    usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+    usbi_warn (ctx, "transfer error: timed out");
     itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
     return LIBUSB_TRANSFER_TIMED_OUT;
   default:
-    usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+    usbi_warn (ctx, "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
     return LIBUSB_TRANSFER_ERROR;
   }
 }
@@ -2148,22 +2355,23 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
   const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
-  const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"};
+  const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL};
   bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+  struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
 
   if (transfer->type > max_transfer_type) {
-    usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+    usbi_err (ctx, "unknown endpoint type %d", transfer->type);
     return LIBUSB_ERROR_INVALID_PARAM;
   }
 
   if (NULL == tpriv) {
-    usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv");
+    usbi_err (ctx, "malformed request is missing transfer priv");
     return LIBUSB_ERROR_INVALID_PARAM;
   }
 
-  usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
+  usbi_dbg (ctx, "handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
 
-  if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
+  if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result || kIOUSBTransactionTimeout == tpriv->result) {
     if (is_isoc && tpriv->isoc_framelist) {
       /* copy isochronous results back */
 
@@ -2262,9 +2470,159 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne
 }
 #endif
 
+#if InterfaceVersion >= 700
+
+/* macOS APIs for getting entitlement values */
+
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+#include <Security/Security.h>
+#else
+typedef struct __SecTask *SecTaskRef;
+extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator);
+extern CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error);
+#endif
+
+static bool darwin_has_capture_entitlements (void) {
+  SecTaskRef task;
+  CFTypeRef value;
+  bool entitled;
+
+  task = SecTaskCreateFromSelf (kCFAllocatorDefault);
+  if (task == NULL) {
+    return false;
+  }
+  value = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.vm.device-access"), NULL);
+  CFRelease (task);
+  entitled = value && (CFGetTypeID (value) == CFBooleanGetTypeID ()) && CFBooleanGetValue (value);
+  if (value) {
+    CFRelease (value);
+  }
+  return entitled;
+}
+
+static int darwin_reload_device (struct libusb_device_handle *dev_handle) {
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+  enum libusb_error err;
+
+  usbi_mutex_lock(&darwin_cached_devices_lock);
+  (*(dpriv->device))->Release(dpriv->device);
+  dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service);
+  if (!dpriv->device) {
+    err = LIBUSB_ERROR_NO_DEVICE;
+  } else {
+    err = LIBUSB_SUCCESS;
+  }
+  usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+  return err;
+}
+
+/* On macOS, we capture an entire device at once, not individual interfaces. */
+
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+  UNUSED(interface);
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+  IOReturn kresult;
+  enum libusb_error err;
+  struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
+  if (HAS_CAPTURE_DEVICE()) {
+  } else {
+    return LIBUSB_ERROR_NOT_SUPPORTED;
+  }
+
+  if (dpriv->capture_count == 0) {
+    usbi_dbg (ctx, "attempting to detach kernel driver from device");
+
+    if (darwin_has_capture_entitlements ()) {
+      /* request authorization */
+      kresult = IOServiceAuthorize (dpriv->service, kIOServiceInteractionAllowed);
+      if (kresult != kIOReturnSuccess) {
+        usbi_warn (ctx, "IOServiceAuthorize: %s", darwin_error_str(kresult));
+        return darwin_to_libusb (kresult);
+      }
+
+      /* we need start() to be called again for authorization status to refresh */
+      err = darwin_reload_device (dev_handle);
+      if (err != LIBUSB_SUCCESS) {
+        return err;
+      }
+    } else {
+      usbi_info (ctx, "no capture entitlements. may not be able to detach the kernel driver for this device");
+      if (0 != geteuid()) {
+        usbi_warn (ctx, "USB device capture requires either an entitlement (com.apple.vm.device-access) or root privilege");
+        return LIBUSB_ERROR_ACCESS;
+      }
+    }
+
+    /* reset device to release existing drivers */
+    err = darwin_reenumerate_device (dev_handle, true);
+    if (err != LIBUSB_SUCCESS) {
+      return err;
+    }
+  }
+  dpriv->capture_count++;
+  return LIBUSB_SUCCESS;
+}
+
+
+static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+  UNUSED(interface);
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+  if (HAS_CAPTURE_DEVICE()) {
+  } else {
+    return LIBUSB_ERROR_NOT_SUPPORTED;
+  }
+
+  dpriv->capture_count--;
+  if (dpriv->capture_count > 0) {
+    return LIBUSB_SUCCESS;
+  }
+
+  usbi_dbg (HANDLE_CTX (dev_handle), "reenumerating device for kernel driver attach");
+
+  /* reset device to attach kernel drivers */
+  return darwin_reenumerate_device (dev_handle, false);
+}
+
+static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+  enum libusb_error ret;
+  if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) {
+    ret = darwin_detach_kernel_driver (dev_handle, iface);
+    if (ret != LIBUSB_SUCCESS) {
+      usbi_info (HANDLE_CTX (dev_handle), "failed to auto-detach the kernel driver for this device, ret=%d", ret);
+    }
+  }
+
+  return darwin_claim_interface (dev_handle, iface);
+}
+
+static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+  enum libusb_error ret;
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+  ret = darwin_release_interface (dev_handle, iface);
+  if (ret != LIBUSB_SUCCESS) {
+    return ret;
+  }
+
+  if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) {
+    ret = darwin_attach_kernel_driver (dev_handle, iface);
+    if (LIBUSB_SUCCESS != ret) {
+      usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret);
+    }
+    /* ignore the error as the interface was successfully released */
+  }
+
+  return LIBUSB_SUCCESS;
+}
+
+#endif
+
 const struct usbi_os_backend usbi_backend = {
         .name = "Darwin",
-        .caps = 0,
+        .caps = USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
         .init = darwin_init,
         .exit = darwin_exit,
         .get_active_config_descriptor = darwin_get_active_config_descriptor,
@@ -2275,8 +2633,6 @@ const struct usbi_os_backend usbi_backend = {
         .close = darwin_close,
         .get_configuration = darwin_get_configuration,
         .set_configuration = darwin_set_configuration,
-        .claim_interface = darwin_claim_interface,
-        .release_interface = darwin_release_interface,
 
         .set_interface_altsetting = darwin_set_interface_altsetting,
         .clear_halt = darwin_clear_halt,
@@ -2289,6 +2645,16 @@ const struct usbi_os_backend usbi_backend = {
 
         .kernel_driver_active = darwin_kernel_driver_active,
 
+#if InterfaceVersion >= 700
+        .detach_kernel_driver = darwin_detach_kernel_driver,
+        .attach_kernel_driver = darwin_attach_kernel_driver,
+        .claim_interface = darwin_capture_claim_interface,
+        .release_interface = darwin_capture_release_interface,
+#else
+        .claim_interface = darwin_claim_interface,
+        .release_interface = darwin_release_interface,
+#endif
+
         .destroy_device = darwin_destroy_device,
 
         .submit_transfer = darwin_submit_transfer,
index b799bfd4429e593fadb95b3e0704ea197a9b3177..7b72fffb89531109ee74b2331ec631b962ce265a 100644 (file)
 #include <IOKit/usb/IOUSBLib.h>
 #include <IOKit/IOCFPlugIn.h>
 
+#if defined(HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H)
+#include <IOKit/usb/IOUSBHostFamilyDefinitions.h>
+#endif
+
 /* IOUSBInterfaceInferface */
 
 /* New in OS 10.12.0. */
-#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
+#if defined (kIOUSBInterfaceInterfaceID800)
 
 #define usb_interface_t IOUSBInterfaceInterface800
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
 #define InterfaceVersion 800
 
 /* New in OS 10.10.0. */
-#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000)
+#elif defined (kIOUSBInterfaceInterfaceID700)
 
 #define usb_interface_t IOUSBInterfaceInterface700
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
 #define InterfaceVersion 700
 
 /* New in OS 10.9.0. */
-#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID650)
 
 #define usb_interface_t IOUSBInterfaceInterface650
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
 #define InterfaceVersion 650
 
 /* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID550)
 
 #define usb_interface_t IOUSBInterfaceInterface550
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
 #define InterfaceVersion 550
 
 /* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBInterfaceInterfaceID500)
 
 #define usb_interface_t IOUSBInterfaceInterface500
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
 #define InterfaceVersion 500
 
 /* New in OS 10.5.0. */
-#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID300)
 
 #define usb_interface_t IOUSBInterfaceInterface300
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
 #define InterfaceVersion 300
 
 /* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID245)
 
 #define usb_interface_t IOUSBInterfaceInterface245
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
 #define InterfaceVersion 245
 
 /* New in OS 10.4.0. */
-#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040)
+#elif defined (kIOUSBInterfaceInterfaceID220)
 
 #define usb_interface_t IOUSBInterfaceInterface220
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
 /* IOUSBDeviceInterface */
 
 /* New in OS 10.9.0. */
-#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#if defined (kIOUSBDeviceInterfaceID650)
 
 #define usb_device_t    IOUSBDeviceInterface650
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID650
 #define DeviceVersion 650
 
 /* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBDeviceInterfaceID500)
 
 #define usb_device_t    IOUSBDeviceInterface500
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID500
 #define DeviceVersion 500
 
 /* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
+#elif defined (kIOUSBDeviceInterfaceID320)
 
 #define usb_device_t    IOUSBDeviceInterface320
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID320
 #define DeviceVersion 320
 
 /* New in OS 10.5.0. */
-#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID300)
 
 #define usb_device_t    IOUSBDeviceInterface300
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID300
 #define DeviceVersion 300
 
 /* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID245)
 
 #define usb_device_t    IOUSBDeviceInterface245
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID245
 #define DeviceVersion 245
 
 /* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030)
+#elif defined (kIOUSBDeviceInterfaceID197)
 
 #define usb_device_t    IOUSBDeviceInterface197
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197
 
 #endif
 
+#if !defined(kIOUSBHostInterfaceClassName)
+#define kIOUSBHostInterfaceClassName "IOUSBHostInterface"
+#endif
+
+#if !defined(kUSBHostMatchingPropertyInterfaceNumber)
+#define kUSBHostMatchingPropertyInterfaceNumber "bInterfaceNumber"
+#endif
+
 #if !defined(IO_OBJECT_NULL)
 #define IO_OBJECT_NULL ((io_object_t) 0)
 #endif
 
+/* Testing availability */
+#ifndef __has_builtin
+  #define __has_builtin(x) 0  // Compatibility with non-clang compilers.
+#endif
+#if __has_builtin(__builtin_available)
+  #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *)
+#else
+  #define HAS_CAPTURE_DEVICE() 0
+#endif
+
 typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
 typedef IONotificationPortRef io_notification_port_t;
 
@@ -161,11 +183,13 @@ struct darwin_cached_device {
   USBDeviceAddress      address;
   char                  sys_path[21];
   usb_device_t        **device;
+  io_service_t          service;
   int                   open_count;
   UInt8                 first_config, active_config, port;
   int                   can_enumerate;
   int                   refcount;
   bool                  in_reenumerate;
+  int                   capture_count;
 };
 
 struct darwin_device_priv {
index b74189b94b3c7f75e3e02f9dff9456986ed90df4..715a2d551c25b6398872a3883a99f9fde7a82472 100644 (file)
@@ -222,9 +222,9 @@ int usbi_wait_for_events(struct libusb_context *ctx,
        usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
        int internal_fds, num_ready;
 
-       usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+       usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
        num_ready = poll(fds, nfds, timeout_ms);
-       usbi_dbg("poll() returned %d", num_ready);
+       usbi_dbg(ctx, "poll() returned %d", num_ready);
        if (num_ready == 0) {
                if (usbi_using_timer(ctx))
                        goto done;
@@ -279,7 +279,7 @@ int usbi_wait_for_events(struct libusb_context *ctx,
                                        continue;
                                /* pollfd was removed between the creation of the fds array and
                                 * here. remove triggered revent as it is no longer relevant. */
-                               usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+                               usbi_dbg(ctx, "fd %d was removed, ignoring raised events", fds[n].fd);
                                fds[n].revents = 0;
                                num_ready--;
                                break;
index adc95b4028f3d42218806eb048d36fa7b3c2d26e..1fa1f0be5cfdff5bb6787d0ac17eb898395e96e5 100644 (file)
@@ -36,7 +36,7 @@ static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer)
 {
        int *completed = transfer->user_data;
        *completed = 1;
-       usbi_dbg("actual_length=%d", transfer->actual_length);
+       usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length);
        /* caller interprets result and frees transfer */
 }
 
index d8ebde4e394d6e1b072f673e281361402bb937b9..fe95d84b60c59eb7e2a1dd6dbe40ca992fc854e6 100644 (file)
@@ -7,7 +7,7 @@
 #define LIBUSB_MINOR 0
 #endif
 #ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 24
+#define LIBUSB_MICRO 26
 #endif
 #ifndef LIBUSB_NANO
 #define LIBUSB_NANO 0
index 0f100a82478c5c09f44f31712680d4a7d6157a8d..dbd5d5f5b1db19641df10726f2e34bb919db2f92 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11584
+#define LIBUSB_NANO 11724